From ca8fcb25b87ace130c2a77fda575afa64c46c4dc Mon Sep 17 00:00:00 2001 From: roboomp Date: Wed, 29 Jul 2026 16:47:38 +0000 Subject: [PATCH] fix(ai): strip every bare resource-exhausted token parseConnectEndStream repeats the default status phrase in the message body, so the trailer reads "resource_exhausted: resource exhausted". Using a global strip removes both occurrences, so the leftover "exhausted" no longer trips the generic quota branch and reintroduces the 30-minute credential block for an otherwise bare status. Fixes #7032 (cherry picked from commit 3d46a042c649e3892d9c68abbc3789a7719f0667) --- packages/ai/src/error/rate-limit.ts | 6 +++--- packages/ai/test/rate-limit-utils.test.ts | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/ai/src/error/rate-limit.ts b/packages/ai/src/error/rate-limit.ts index 4ec6cbbf6..7252e0dcf 100644 --- a/packages/ai/src/error/rate-limit.ts +++ b/packages/ai/src/error/rate-limit.ts @@ -25,7 +25,7 @@ const OPENROUTER_DAILY_FREE_LIMIT_PATTERN = /\bfree[-_ ]models[-_ ]per[-_ ]day\b // while HTTP bodies use the phrase ("resource exhausted"). Strip either form // before classifying explicit details; an otherwise opaque status is transient // model capacity, while quota/rate-limit/server wording remains authoritative. -const RESOURCE_EXHAUSTED_PATTERN = /resource.?exhausted/i; +const RESOURCE_EXHAUSTED_PATTERN = /resource.?exhausted/gi; /** * Classify a rate-limit error message into a reason category. @@ -38,8 +38,8 @@ const RESOURCE_EXHAUSTED_PATTERN = /resource.?exhausted/i; */ export function parseRateLimitReason(errorMessage: string): RateLimitReason { const lowerWithStatus = errorMessage.toLowerCase(); - const hasResourceExhaustedStatus = RESOURCE_EXHAUSTED_PATTERN.test(lowerWithStatus); - const lower = hasResourceExhaustedStatus ? lowerWithStatus.replace(RESOURCE_EXHAUSTED_PATTERN, "") : lowerWithStatus; + const lower = lowerWithStatus.replace(RESOURCE_EXHAUSTED_PATTERN, ""); + const hasResourceExhaustedStatus = lower !== lowerWithStatus; // Antigravity / Cloud Code Assist surface multi-hour daily-quota exhaustion as // "You have exhausted your capacity on this model. Your quota will reset after …". diff --git a/packages/ai/test/rate-limit-utils.test.ts b/packages/ai/test/rate-limit-utils.test.ts index 1d970db52..6f25cad55 100644 --- a/packages/ai/test/rate-limit-utils.test.ts +++ b/packages/ai/test/rate-limit-utils.test.ts @@ -35,6 +35,15 @@ describe("parseRateLimitReason", () => { expect(parseRateLimitReason("Connect error resource_exhausted: Error")).toBe("MODEL_CAPACITY_EXHAUSTED"); }); + // parseConnectEndStream repeats the default status phrase in the message body: + // `Connect error resource_exhausted: resource exhausted`. Both tokens must be + // stripped so the leftover "exhausted" doesn't trip the generic quota branch. + it("classifies repeated bare resource-exhausted tokens as MODEL_CAPACITY_EXHAUSTED", () => { + expect(parseRateLimitReason("Connect error resource_exhausted: resource exhausted")).toBe( + "MODEL_CAPACITY_EXHAUSTED", + ); + }); + it("keeps explicit quota details authoritative after resource_exhausted", () => { expect(parseRateLimitReason("Connect error resource_exhausted: Quota exceeded for this account")).toBe( "QUOTA_EXHAUSTED",