fix(failover): classify ZenMux quota-refresh 402 as rate_limit (#43917)

Merged via squash.

Prepared head SHA: 1d58a36a77
Co-authored-by: bwjoke <1284814+bwjoke@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
This commit is contained in:
bwjoke
2026-03-13 05:06:43 +08:00
committed by GitHub
parent d93db0fc13
commit fd568c4f74
3 changed files with 19 additions and 0 deletions

View File

@@ -204,6 +204,13 @@ describe("failover-error", () => {
message: "Workspace spend limit reached. Contact your admin.",
}),
).toBe("rate_limit");
expect(
resolveFailoverReasonFromError({
status: 402,
message:
"You have reached your subscription quota limit. Please wait for automatic quota refresh in the rolling time window, upgrade to a higher plan, or use a Pay-As-You-Go API Key for unlimited access. Learn more: https://zenmux.ai/docs/guide/subscription.html",
}),
).toBe("rate_limit");
expect(
resolveFailoverReasonFromError({
status: 402,

View File

@@ -288,6 +288,13 @@ function hasExplicit402BillingSignal(text: string): boolean {
);
}
function hasQuotaRefreshWindowSignal(text: string): boolean {
return (
text.includes("subscription quota limit") &&
(text.includes("automatic quota refresh") || text.includes("rolling time window"))
);
}
function hasRetryable402TransientSignal(text: string): boolean {
const hasPeriodicHint = includesAnyHint(text, PERIODIC_402_HINTS);
const hasSpendLimit = text.includes("spend limit") || text.includes("spending limit");
@@ -313,6 +320,10 @@ function classify402Message(message: string): PaymentRequiredFailoverReason {
return "billing";
}
if (hasQuotaRefreshWindowSignal(normalized)) {
return "rate_limit";
}
if (hasExplicit402BillingSignal(normalized)) {
return "billing";
}