mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 22:00:40 +00:00
fix(failover): disambiguate periodic limit errors
This commit is contained in:
@@ -123,6 +123,19 @@ describe("failover-error", () => {
|
||||
message: ZHIPUAI_WEEKLY_MONTHLY_LIMIT_EXHAUSTED_MESSAGE,
|
||||
}),
|
||||
).toBe("rate_limit");
|
||||
expect(
|
||||
resolveFailoverReasonFromError({
|
||||
message: "LLM error: monthly limit reached",
|
||||
}),
|
||||
).toBe("rate_limit");
|
||||
});
|
||||
|
||||
it("keeps raw-text 402 weekly/monthly limit errors in billing", () => {
|
||||
expect(
|
||||
resolveFailoverReasonFromError({
|
||||
message: "402 Payment Required: Weekly/Monthly Limit Exhausted",
|
||||
}),
|
||||
).toBe("billing");
|
||||
});
|
||||
|
||||
it("infers format errors from error messages", () => {
|
||||
|
||||
@@ -540,6 +540,9 @@ describe("classifyFailoverReason", () => {
|
||||
expect(
|
||||
classifyFailoverReason("HTTP 402 payment required. Your limit exhausted for this plan."),
|
||||
).toBe("billing");
|
||||
expect(classifyFailoverReason("402 Payment Required: Weekly/Monthly Limit Exhausted")).toBe(
|
||||
"billing",
|
||||
);
|
||||
expect(classifyFailoverReason(INSUFFICIENT_QUOTA_PAYLOAD)).toBe("billing");
|
||||
expect(classifyFailoverReason("deadline exceeded")).toBe("timeout");
|
||||
expect(classifyFailoverReason("request ended without sending any chunks")).toBe("timeout");
|
||||
@@ -595,8 +598,10 @@ describe("classifyFailoverReason", () => {
|
||||
"LLM error 1310: Weekly/Monthly Limit Exhausted. Your limit will reset at 2026-03-06 22:19:54 (request_id: 20260303141547610b7f574d1b44cb)",
|
||||
),
|
||||
).toBe("rate_limit");
|
||||
// Independent coverage for /weekly\/monthly limit/i (no generic "limit exhausted")
|
||||
// Independent coverage for broader periodic limit patterns.
|
||||
expect(classifyFailoverReason("LLM error: weekly/monthly limit reached")).toBe("rate_limit");
|
||||
expect(classifyFailoverReason("LLM error: monthly limit reached")).toBe("rate_limit");
|
||||
expect(classifyFailoverReason("LLM error: daily limit exceeded")).toBe("rate_limit");
|
||||
});
|
||||
it("classifies permanent auth errors as auth_permanent", () => {
|
||||
expect(classifyFailoverReason("invalid_api_key")).toBe("auth_permanent");
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
isAuthPermanentErrorMessage,
|
||||
isBillingErrorMessage,
|
||||
isOverloadedErrorMessage,
|
||||
isPeriodicUsageLimitErrorMessage,
|
||||
isRateLimitErrorMessage,
|
||||
isTimeoutErrorMessage,
|
||||
matchesFormatErrorPattern,
|
||||
@@ -842,6 +843,9 @@ export function classifyFailoverReason(raw: string): FailoverReason | null {
|
||||
if (isJsonApiInternalServerError(raw)) {
|
||||
return "timeout";
|
||||
}
|
||||
if (isPeriodicUsageLimitErrorMessage(raw)) {
|
||||
return isBillingErrorMessage(raw) ? "billing" : "rate_limit";
|
||||
}
|
||||
if (isRateLimitErrorMessage(raw)) {
|
||||
return "rate_limit";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
type ErrorPattern = RegExp | string;
|
||||
|
||||
const PERIODIC_USAGE_LIMIT_RE =
|
||||
/\b(?:daily|weekly|monthly)(?:\/(?:daily|weekly|monthly))* (?:usage )?limit(?:s)?(?: (?:exhausted|reached|exceeded))?\b/i;
|
||||
|
||||
const ERROR_PATTERNS = {
|
||||
rateLimit: [
|
||||
/rate[_ ]limit|too many requests|429/,
|
||||
@@ -9,7 +12,6 @@ const ERROR_PATTERNS = {
|
||||
"quota exceeded",
|
||||
"resource_exhausted",
|
||||
"usage limit",
|
||||
/weekly\/monthly limit/i,
|
||||
/\btpm\b/i,
|
||||
"tokens per minute",
|
||||
],
|
||||
@@ -118,6 +120,10 @@ export function isTimeoutErrorMessage(raw: string): boolean {
|
||||
return matchesErrorPatterns(raw, ERROR_PATTERNS.timeout);
|
||||
}
|
||||
|
||||
export function isPeriodicUsageLimitErrorMessage(raw: string): boolean {
|
||||
return PERIODIC_USAGE_LIMIT_RE.test(raw);
|
||||
}
|
||||
|
||||
export function isBillingErrorMessage(raw: string): boolean {
|
||||
const value = raw.toLowerCase();
|
||||
if (!value) {
|
||||
|
||||
Reference in New Issue
Block a user