mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix: narrow 402 rate-limit matcher to prevent billing misclassification
The original implementation used isRateLimitErrorMessage(), which matches phrases like 'quota exceeded' that legitimately appear in billing errors. This commit replaces it with a narrow, 402-specific matcher that requires BOTH retry language (try again/retry/temporary/cooldown) AND limit terminology (usage limit/rate limit/organization usage). Prevents misclassification of errors like: 'HTTP 402: exceeded quota, please add credits' -> billing (not rate_limit) Added regression test for the ambiguous case.
This commit is contained in:
@@ -51,6 +51,13 @@ describe("failover-error", () => {
|
|||||||
message: "insufficient credits — please top up your account",
|
message: "insufficient credits — please top up your account",
|
||||||
}),
|
}),
|
||||||
).toBe("billing");
|
).toBe("billing");
|
||||||
|
// Ambiguous "quota exceeded" + billing signal → billing wins
|
||||||
|
expect(
|
||||||
|
resolveFailoverReasonFromError({
|
||||||
|
status: 402,
|
||||||
|
message: "HTTP 402: You have exceeded your current quota. Please add more credits.",
|
||||||
|
}),
|
||||||
|
).toBe("billing");
|
||||||
expect(resolveFailoverReasonFromError({ statusCode: "429" })).toBe("rate_limit");
|
expect(resolveFailoverReasonFromError({ statusCode: "429" })).toBe("rate_limit");
|
||||||
expect(resolveFailoverReasonFromError({ status: 403 })).toBe("auth");
|
expect(resolveFailoverReasonFromError({ status: 403 })).toBe("auth");
|
||||||
expect(resolveFailoverReasonFromError({ status: 408 })).toBe("timeout");
|
expect(resolveFailoverReasonFromError({ status: 408 })).toBe("timeout");
|
||||||
|
|||||||
@@ -261,10 +261,22 @@ export function classifyFailoverReasonFromHttpStatus(
|
|||||||
|
|
||||||
if (status === 402) {
|
if (status === 402) {
|
||||||
// Some providers (e.g. Anthropic Claude Max plan) surface temporary
|
// Some providers (e.g. Anthropic Claude Max plan) surface temporary
|
||||||
// usage/rate-limit failures as HTTP 402. Prefer the explicit rate-limit
|
// usage/rate-limit failures as HTTP 402. Use a narrow matcher for
|
||||||
// signal from the payload text when available (#30484).
|
// temporary limits to avoid misclassifying billing failures (#30484).
|
||||||
if (message && isRateLimitErrorMessage(message)) {
|
if (message) {
|
||||||
return "rate_limit";
|
const lower = message.toLowerCase();
|
||||||
|
// Temporary usage limit signals: retry language + usage/limit terminology
|
||||||
|
const hasTemporarySignal =
|
||||||
|
(lower.includes("try again") ||
|
||||||
|
lower.includes("retry") ||
|
||||||
|
lower.includes("temporary") ||
|
||||||
|
lower.includes("cooldown")) &&
|
||||||
|
(lower.includes("usage limit") ||
|
||||||
|
lower.includes("rate limit") ||
|
||||||
|
lower.includes("organization usage"));
|
||||||
|
if (hasTemporarySignal) {
|
||||||
|
return "rate_limit";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return "billing";
|
return "billing";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user