fix: guard bare 402 failover detection

This commit is contained in:
Altay
2026-04-16 16:47:15 +03:00
parent c96b9a3a07
commit d43aa3a2d9
2 changed files with 25 additions and 1 deletions

View File

@@ -1020,6 +1020,8 @@ describe("classifyFailoverReason", () => {
"402 No available asset for API access, please purchase a subscription",
),
).toBe("billing");
expect(classifyFailoverReason("402 items found in the database")).toBeNull();
expect(classifyFailoverReason("402 room is available")).toBeNull();
expect(classifyFailoverReason(INSUFFICIENT_QUOTA_PAYLOAD)).toBe("billing");
expect(classifyFailoverReason("deadline exceeded")).toBe("timeout");
expect(classifyFailoverReason("request ended without sending any chunks")).toBe("timeout");

View File

@@ -302,6 +302,8 @@ const RAW_402_MARKER_RE =
const BARE_LEADING_402_RE = /^\s*402\b/i;
const LEADING_402_WRAPPER_RE =
/^(?:error[:\s-]+)?(?:(?:http\s*)?402(?:\s+payment required)?|payment required)(?:[:\s-]+|$)/i;
const LEADING_BARE_402_RE = /^\s*402\s+\S/i;
const LEADING_402_PAYMENT_REQUIRED_RE = /^\s*402\s+payment required\b/i;
const TIMEOUT_ERROR_CODES = new Set([
"ETIMEDOUT",
"ESOCKETTIMEDOUT",
@@ -464,7 +466,6 @@ function isOAuthCallbackTimeoutMessage(raw: string): boolean {
function isOAuthCallbackValidationMessage(raw: string): boolean {
return /\bcallback_validation_failed\b/i.test(raw);
}
function includesAnyHint(text: string, hints: readonly string[]): boolean {
return hints.some((hint) => text.includes(hint));
}
@@ -539,6 +540,18 @@ function classify402Message(message: string): PaymentRequiredFailoverReason {
return "billing";
}
function hasBareLeading402Signal(text: string): boolean {
return (
hasQuotaRefreshWindowSignal(text) ||
hasExplicit402BillingSignal(text) ||
isRateLimitErrorMessage(text) ||
hasRetryable402TransientSignal(text) ||
text.includes("used up your points") ||
text.includes("no available asset for api access") ||
(text.includes("purchase") && text.includes("subscription"))
);
}
function classifyFailoverReasonFrom402Text(raw: string): PaymentRequiredFailoverReason | null {
if (RAW_402_MARKER_RE.test(raw)) {
return classify402Message(raw);
@@ -550,6 +563,15 @@ function classifyFailoverReasonFrom402Text(raw: string): PaymentRequiredFailover
if (!normalized || !hasKnownBareLeading402Signal(normalized)) {
return null;
}
const normalized = normalize402Message(raw);
if (
normalized &&
LEADING_BARE_402_RE.test(raw) &&
!LEADING_402_PAYMENT_REQUIRED_RE.test(raw) &&
!hasBareLeading402Signal(normalized)
) {
return null;
}
return classify402Message(raw);
}