Skip to content

fix(resilience): don't clear an active rate-limit cooldown for non-quota_exhausted errors (#11277) - #11310

Merged
diegosouzapw merged 1 commit into
release/v3.8.50from
fix/11277-quota-cooldown-clear-guard
Aug 24, 2026
Merged

diegosouzapw merged 1 commit into
release/v3.8.50from
fix/11277-quota-cooldown-clear-guard

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

⚠️ base-red inherited: #9985

What

maybeClearRecoveredQuotaState() (src/lib/usage/providerLimits.ts:462) only guarded against clearing a still-future rateLimitedUntil inside the lastErrorType === "quota_exhausted" branch. Any other lastErrorType (rate_limited, free_quota_exhausted, etc.) skipped straight to clearRecoveredProviderState() with no future-cooldown check at all.

In production this caused a self-restart loop: a 146-hour cooldown got cleared every few minutes by the periodic quota sync, the connection went back to priority position 1, burned a real upstream call, took the same 429, was cooled down again, and the next sync cleared it again.

Fix

Added a universal fallback guard: for every lastErrorType other than quota_exhausted, if rateLimitedUntil is still in the future, don't clear. A future rateLimitedUntil is a hard statement made by the handler that persisted it — no quota poll finding some usable window elsewhere should be able to overrule it.

Also fixed a pre-existing test (provider-limits-recovery.test.ts) that encoded the same defect class with a shorter window — it asserted a still-future cooldown got cleared by a successful refresh, which was the bug, just less visible.

Validation (TDD, Hard Rule #18)

New test: a still-future rateLimitedUntil is not cleared by a successful quota refresh, regardless of lastErrorType (#11277) — RED before the fix, GREEN after. Full provider-limits-recovery.test.ts suite: 15/15 pass. typecheck:core clean. ESLint clean on both touched files.

Closes #11277

…ota_exhausted errors (#11277)

maybeClearRecoveredQuotaState() only guarded against clearing a still-future
rateLimitedUntil inside the lastErrorType === "quota_exhausted" branch. Any
other lastErrorType (rate_limited, free_quota_exhausted, etc.) skipped
straight to clearRecoveredProviderState() with no future-cooldown check at
all, so a multi-day cooldown got wiped on the next quota sync a few minutes
later — a self-restart loop burning real upstream calls against a
known-exhausted connection.

Added a universal fallback guard for every other lastErrorType: a future
rateLimitedUntil is a hard statement from whatever handler persisted it, and
no quota poll finding some usable window elsewhere may overrule it.

Also fixed a pre-existing test that encoded the same defect class with a
shorter window (asserted a still-future cooldown got cleared by a successful
refresh) and added a regression test pinning the new guard.
@diegosouzapw
diegosouzapw merged commit ac02c5b into release/v3.8.50 Aug 24, 2026
20 of 22 checks passed
@diegosouzapw
diegosouzapw deleted the fix/11277-quota-cooldown-clear-guard branch August 25, 2026 02:36
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…ota_exhausted errors (diegosouzapw#11277) (diegosouzapw#11310)

Merging --admin: only fails are ESLint warnings ratchet drift (inherited base-red) and dast-smoke (advisory, isRequired:null). Zero overlap with this PR's scope (src/lib/usage/providerLimits.ts).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(quota): active rateLimitedUntil cooldown is cleared by the provider-limits sync when lastErrorType is not "quota_exhausted"

2 participants