From ea9f15db27cb67c2932171e11e786f5fb9677846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89der=20Costa?= Date: Thu, 6 Aug 2026 06:05:58 -0300 Subject: [PATCH] fix: treat zero-reset Antigravity 429s as transient (#8626) Validated in local merge-train T7 (ungrouped batch 2) --- open-sse/services/antigravity429Engine.ts | 8 ++++++++ tests/unit/antigravity-429-quota-cooldown.test.ts | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/open-sse/services/antigravity429Engine.ts b/open-sse/services/antigravity429Engine.ts index a29b0dba2e..7c859c673b 100644 --- a/open-sse/services/antigravity429Engine.ts +++ b/open-sse/services/antigravity429Engine.ts @@ -61,6 +61,14 @@ const FULL_QUOTA_COOLDOWN_MS = 24 * 60 * 60 * 1000; // 24 hours export function classify429(errorMessage: string): Category { const lower = (errorMessage || "").toLowerCase(); + // Cloud Code may report an exhausted-capacity message with a zero reset + // window for a burst/RPM throttle. The explicit zero reset is stronger + // evidence than the generic wording, so retry briefly instead of applying + // the durable quota cooldown. + if (/\breset\s+(?:after|in)\s+0s\b/.test(lower)) { + return "rate_limited"; + } + // Check for quota exhaustion first (most specific) for (const kw of QUOTA_EXHAUSTED_KEYWORDS) { if (lower.includes(kw)) return "quota_exhausted"; diff --git a/tests/unit/antigravity-429-quota-cooldown.test.ts b/tests/unit/antigravity-429-quota-cooldown.test.ts index eab627215b..eaf0a6e09b 100644 --- a/tests/unit/antigravity-429-quota-cooldown.test.ts +++ b/tests/unit/antigravity-429-quota-cooldown.test.ts @@ -70,6 +70,16 @@ test("classify429: standard Gemini rate limit 'resource has been exhausted' -> r ); }); +test("classify429: exhausted capacity with reset after 0s is rate_limited", () => { + const message = "You have exhausted your capacity on this model. Your quota will reset after 0s."; + const category = classify429(message); + assert.equal(category, "rate_limited"); + + const decision = decide429(category, 2_000); + assert.equal(decision.kind, "soft_retry"); + assert.equal(decision.retryAfterMs, 2_000); +}); + // ── DB persistence (the missing wire — Bug #2) ─────────────────────────────── test("markConnectionQuotaExhausted persists 24h cooldown; isConnectionRateLimited returns true", async () => {