Repository navigation
[Bug]: generic OAuth 429 retry budget can starve later accounts in a multi-account pool #5880
Description
Area
Authentication and account pool
Summary
Generic OAuth 429 failover and the request-wide send budget currently disagree about what is being bounded.
On current main (e70b3d86fb1201d7951dfeb25e09b7871c433047, 2.66.0), a provider such as Google Antigravity can consume the normal transient ladder on the current credential before the generic OAuth rotator sees the terminal 429. The default request execution budget is still four physical model sends.
With a multi-account pool, that means valid same-account transient retries can spend the request budget before later logged-in accounts get a chance to serve the turn. This defeats the intent of #2568 when there are more than two usable accounts.
Related history: #2568, #4709, #3375.
Reproduction
Use a generic OAuth provider with four distinct eligible accounts and an HTTP adapter with the standard transient 429 policy (Google Antigravity is one concrete case).
Make the upstream return a transient 429 (not a hard quota-exhausted body) for every physical send.
The first credential consumes the normal transient retry ladder. By the time generic OAuth rotation runs, the shared request budget has too little capacity to traverse the remaining stored accounts.
A deterministic harness makes this visible without generating a real rate-limit storm: record the Authorization identity on each physical send while returning synthetic 429 responses.
Expected behavior
Keep the platform retry/failover ordering consistent:
- bounded transient retries on the current credential;
- if it remains 429, rotate to the next eligible OAuth account;
- apply the same bounded retry policy to that account;
- continue through the usable roster;
- only after the roster is exhausted should a combo degrade to another model/provider.
Hard quota exhaustion should still skip useless same-account retries and rotate according to the existing quota classifier.
An explicit caller-supplied send ceiling should remain authoritative and must not be silently widened.
Root cause / implementation notes
There are three interacting seams:
- The generic OAuth rotation cap and the request-wide physical-send budget are independent. A roster can have several eligible accounts while the request still owns only the default guarded send allowance.
- A same-request rotation ceiling derived from the currently eligible roster can shrink as the same request writes cooldowns.
- Generic auth-recovery call sites reserve with diagnostic target keys (for example
provider|model|...oauth-429) while adapters reserve physical sends against the request URL. If that diagnostic key becomeslastTargetKey, a same-provider credential rotation can be misclassified as a target/model transition and later retries may be refused.
A working shape is to derive any roster expansion from the same canonical transient-attempt constant, keep the same-request roster ceiling stable while selection still filters live cooldowns, and preserve the physical target identity for same-provider auth recovery. Expansion should only apply to the normal ingress-owned budget, not exact caller-provided ceilings.
Validation
In a four-account deterministic Antigravity regression, the intended transient sequence is bounded as:
3, 3, 3, 3
across four distinct credential/project pairs, followed by the final 429.
The same suite should also pin:
- single-credential 429 recovery on attempt 2/3;
- hard quota exhaustion does not waste retries;
- explicit custom send ceilings remain unchanged.
Checks
- I searched existing issues ([Feature]: Fail over to another logged-in account on 429 for any OAuth provider with 2+ accounts, not just Codex and opt-in Anthropic #2568, [Bug]: the generic-OAuth hop permit is not threaded to the adapter, so one replay can be charged twice #4709, [Feature]: complete the OAuth account-pool lifecycle — session affinity, 401/403 rotation, pool health, and stable reset-credit identity #3375).
- No real credentials, account IDs, emails, tokens, or host paths are included.
- Reproduction can be fully deterministic with a mocked upstream.
Activity
fix: bug-PR merge train batch 7 (multi-account OAuth 429 budget with …
lidge-jun commentedon Sep 26, 2026
Fixed on
devby #5924 (mergef32f9aabd7), which carried #5916. Multi-account OAuth pools now fund each eligible account's normal three-send transient ladder, so a 429 on the first account no longer starves the rest. To keep one request from fanning out across a large roster, the default ingress ceiling is capped at six accounts (18 sends). The focused #5880 tests pass on currentdev. Closing; please reopen if it still reproduces on a build that includesf32f9aabd7.