Skip to content

[Feature]: Generic OAuth account-pool failover with session affinity and quota-aware selection #695

Description

@luwei1990

Area

Authentication / OAuth account pools / routing

Goal

Implement one generic OAuth account-pool selection and failover engine that provider integrations can reuse.

This issue owns the runtime policy for:

  • session/account affinity;
  • account-health classification used for routing;
  • failover when the assigned account becomes unusable;
  • quota-aware account ranking where trustworthy quota evidence exists;
  • deterministic handling of unknown quota;
  • cooldown and eventual re-entry of temporarily limited accounts;
  • observable, bounded explanations for account-selection decisions.

Provider-specific account-management UI, import formats, and quota probes belong in their respective provider issues.

Current status

The routing policy has been defined and accepted.

The remaining work is implementation of the generic OAuth-pool runtime. Existing provider-specific pool implementations must not become the de facto generic contract by copy/paste.

Antigravity-specific work is now separated cleanly from this generic runtime:

This issue remains open because the generic OAuth failover/affinity engine itself is not complete.

Accepted routing policy

Hard failover

Break affinity when the currently assigned account is positively known to be unusable for the request.

Relevant signals include:

429
  rate limit / quota exhaustion

401
  credential no longer usable

403
  only when provider-specific classification establishes that the account
  is unusable; generic 403 must not automatically poison an account

Provider errors must be classified deliberately. An unrelated upstream failure must not incorrectly mark an account unhealthy.

Hard-error failover is the correctness path.

Session affinity

Healthy conversations stay on their assigned account.

Ordinary requests must not rotate across accounts merely for load balancing.

Conceptually:

new conversation
  -> choose eligible account
  -> bind affinity

later request
  -> same healthy account

assigned account becomes unusable
  -> break affinity
  -> choose next eligible account
  -> bind replacement

In-flight requests and concurrent sessions must not corrupt or accidentally overwrite affinity state.

Quota-aware selection

When fresh, account-scoped quota evidence is available, it may improve account selection.

The accepted policy permits deprioritizing an account at or above approximately 95% detected quota usage.

This is an optimization, not a correctness dependency.

A provider with no usable quota probe must still support hard-error failover.

Stale quota must not be treated as current quota.

Unknown quota

Unknown quota remains a first-class state.

Suggested ordering:

known healthy
>
healthy but quota-unverified
>
known depleted / cooling down / unusable

Unknown must not mean:

0% used

and must not mean:

fully healthy capacity

Account A's quota or failure state must never be projected onto account B.

Cooldown and re-entry

A temporary 429 or quota exhaustion must not permanently remove an account from the pool.

The generic runtime needs an explicit lifecycle such as:

eligible
  -> temporarily limited
  -> cooling down
  -> eligible again after validated reset/re-probe evidence

Re-entry must be bounded and deterministic.

#822 and #823 remain separate reset-lifecycle features. Their background redemption/activation behavior must not be required for generic failover correctness.

No healthy alternative

If the assigned account becomes unusable and no eligible alternative exists:

  • stop retrying after a bounded number of attempts;
  • return the appropriate provider failure;
  • do not loop through the pool indefinitely;
  • do not silently revive an account still known to be unusable.

Priority relationship

#821 implemented user-defined priority ordering for the existing automatic account transition surface.

The generic OAuth-pool engine should be able to consume an explicit priority signal where applicable without making priority configuration mandatory.

Priority is an input to candidate selection, not a replacement for:

  • health classification;
  • affinity;
  • cooldown;
  • quota evidence;
  • failover correctness.

Provider integration contract

Provider integrations should supply normalized evidence such as:

credential usable / requires reauth
quota known / unknown
quota utilization
reset known / unknown
temporarily limited
cooldown state
provider-specific account-unusable classification

The generic pool should own the selection policy.

Do not implement separate Antigravity, Anthropic, Kimi, or other provider-specific copies of the same routing state machine.

Observability and privacy

Operators should be able to determine why an account was:

selected
kept because of affinity
skipped
deprioritized
cooled down
failed over
re-entered

without exposing:

  • access tokens;
  • refresh tokens;
  • API keys;
  • OAuth authorization material;
  • unsafe account identifiers;
  • raw provider quota payloads;
  • unbounded upstream error bodies.

Use safe opaque account references where attribution is required.

Related issues

Acceptance criteria

  • A reusable generic OAuth account-selection/failover engine exists.
  • Healthy conversations preserve account affinity.
  • 429 / recognized quota exhaustion can trigger bounded same-request failover.
  • Authentication failures trigger failover only when the account is actually unusable.
  • Generic 403 responses are not blindly classified as account exhaustion.
  • Unknown-quota accounts are represented and ranked explicitly.
  • Fresh account-scoped quota can optionally influence selection without becoming required for correctness.
  • Temporarily limited accounts have a deterministic cooldown and re-entry path.
  • No-alternative behavior is bounded and tested.
  • Concurrent requests cannot corrupt affinity or selection state.
  • Provider integrations reuse the generic engine instead of duplicating routing policy.
  • Completed priority-ordering work from [Feature] Let users define the priority order for automatic account failover in the Codex pool #821 can be consumed as an optional selection input.
  • Account-selection decisions are observable without exposing credential material.
  • Regression coverage includes healthy affinity, 429 failover, auth-failure failover, unknown quota, cooldown/re-entry, concurrency, and no healthy alternative.
  • Accepted implementation is merged into dev.

Activity

  1. github-actions commented on Jul 29, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: English.

  2. added
    account-poolOAuth, credentials, Codex pool, quota, failover, plans
    on Aug 1, 2026
  3. Ingwannu commented on Aug 1, 2026

    @Ingwannu
    Owner

    Multiple Antigravity accounts can already be stored and selected manually, but "automatically" is not specific enough to define safe routing behavior.

    Please clarify the trigger and invariants: quota percentage, upstream 429/402, authentication failure, or round-robin; whether an existing conversation must stay on its account; how unknown quota is handled; and whether the requested policy is Antigravity-only or should use the generic OAuth account pool. Automatic background use is out of scope unless explicitly designed. Keeping this open for those details rather than guessing at a policy.

  4. lidge-jun commented on Aug 2, 2026

    @lidge-jun
    Owner

    Triage status: useful capability, but we need the policy defined before implementation. Could you spell out: (1) the exact trigger conditions for an automatic switch (which signals, which thresholds), (2) affinity rules while requests are in flight, (3) behavior when an account's quota state is unknown, and (4) whether this should be Antigravity-specific or a generic pool mechanism? Answers to those four determine the design.

  5. added
    needs-infoWaiting on reporter for a concrete spec or reproduction
    on Aug 3, 2026
  6. luwei1990 commented on Aug 6, 2026

    @luwei1990
    Author

    Proposed Failover & Routing Policy

    1. Trigger Signals & Thresholds

    • Primary: Switch immediately on upstream errors 429 (Rate Limit) or 401/403 (Auth invalid).
    • Secondary (Optional): Pre-emptive switch when detected quota reaches $\ge 95%$.

    2. Session Affinity

    • Standard conversations should stick to the assigned account during an active session unless a hard error (429/401) occurs, at which point fallback to a healthy account in the pool happens automatically.

    3. Unknown Quota Handling

    • Treat unknown quota accounts as "Healthy but unverified", ranking them lower than accounts with known healthy quota, but above depleted/errored accounts.

    4. Scope

    • Implement via the generic OAuth account pool mechanism so other providers can benefit from identical failover logic in the future.
  7. lidge-jun commented on Aug 6, 2026

    @lidge-jun
    Owner

    Thanks — this answers all four questions, and it is the specification this needed.

    Recording the policy as accepted design input:

    • Trigger: hard switch on upstream 429 and 401/403; optional pre-emptive switch at ≥95% detected quota.
    • Affinity: a conversation stays on its assigned account for the session, and fails over only on a hard error.
    • Unknown quota: "healthy but unverified" — ranked below known-healthy, above depleted or errored.
    • Scope: the generic OAuth account pool, not an Antigravity-only path.

    The scoping answer is the important one. Building this on the generic pool means the same failover logic serves other OAuth providers instead of creating a second, divergent mechanism next to the existing Codex pool — that is the right call and it is what makes this implementable.

    Two things still need deciding before code, and they follow from your own rules rather than contradicting them:

    1. Re-entry after failover. A 429 account is not permanently dead. The pool needs a cooldown and a re-probe path, otherwise a busy pool degrades to one survivor by the end of the day. Reset-window handling is being discussed in [Research] Provider-specific post-reset activation to anchor quota windows #823 and [Feature]: opt-in crash-safe reset-credit auto-redemption before expiry #822; this issue should consume whatever those settle on rather than inventing a third policy.
    2. The pre-emptive 95% threshold depends on quota being observable. Where quota is unknown or stale, that rule silently does nothing — which is acceptable, but it means the 429 path is the real guarantee and the threshold is an optimization. Worth stating that way so the feature is not oversold.

    Keeping this open as the failover-policy issue. #1062 tracks the pooling and quota-statistics surface for Antigravity, and #821 tracks user-defined priority ordering on top of automatic selection — all three want the same underlying pool, so the runtime slice should land once and be shared.

    No implementation timeline promised. This is now unblocked on design, which it was not before.

  8. agentHits commented on Aug 7, 2026

    @agentHits
    Contributor

    @Wibias @lidge-jun The most important task is to implement a feature for importing accounts from any subscription—specifically, subscriptions sourced from the Cockpit Tools application. This application stores subscription data, which we can export in the following format: [
    {
    "email": "jimguerrero@gmail.com",
    "refresh_token": "1//0cEqtAXq6tRLmCgYIARAAGAwSNwF-D8IrR5oO9j05ZXkGS0FRW9FMEjOwLyae9aIj5nGwfo4-HcTP1LLim_3e2djmCAw_Rm3VzCU"
    }
    ] — and this import capability needs to be integrated into OpenCodeX via the import function. This will allow us to easily manage accounts by simply adding them to the OpenCodeX system, making the process convenient for everyone.

  9. removed
    needs-infoWaiting on reporter for a concrete spec or reproduction
    on Aug 7, 2026
  10. changed the title [-][Feature]: Support switching antigravity accounts automatically[/-] [+][Feature]: Generic OAuth account-pool failover with session affinity and quota-aware selection[/+] on Aug 7, 2026
  11. Wibias commented on Aug 7, 2026

    @Wibias
    Contributor

    Goal

    Implement a generic OAuth account-pool failover policy that can be shared by Antigravity and other OAuth-backed providers.

    This issue tracks the runtime account-selection and failover policy.

    Provider-specific account-management UI, quota presentation, and import formats belong in their respective provider issues.

    Accepted routing policy

    Hard failover

    Switch away from the active account when the upstream returns an error indicating the account is currently unusable:

    • 429 — rate limit or quota exhaustion;
    • 401 — authentication is no longer usable;
    • 403 — authentication/account access is rejected in a way that makes the account unusable for the request.

    The implementation should classify errors carefully rather than treating every provider-side failure as an account-health failure.

    Optional proactive selection

    When reliable quota information is available, an account at or above 95% detected quota usage may be deprioritized before a hard failure occurs.

    This is an optimization, not the correctness guarantee.

    If quota information is missing, stale, or not available for a provider, hard-error failover remains the authoritative path.

    Session affinity

    A conversation should remain on its assigned account while that account remains healthy.

    OpenCodex should not rotate accounts between ordinary requests simply for load balancing.

    Affinity should be broken only when the assigned account becomes unusable according to the failover policy.

    Unknown quota

    An account whose quota state is unknown should be treated as:

    healthy but unverified
    

    Suggested ranking:

    known healthy
    >
    healthy but unverified
    >
    depleted / cooling down / errored
    

    Unknown quota must not automatically mean either exhausted or fully healthy.

    Cooldown and re-entry

    An account that hits a temporary quota/rate limit must not be permanently removed from the pool.

    The pool needs a cooldown and re-probe/re-entry path.

    Reset-window and proactive reactivation behavior should reuse the lifecycle defined by #822 and #823 rather than introducing a separate third policy.

    Scope

    This should be implemented in the generic OAuth account-pool layer so provider integrations can reuse a single selection and failover engine.

    This issue owns:

    • account health classification used for routing;
    • session affinity;
    • hard failover;
    • optional quota-aware selection;
    • deterministic ranking when quota information is unknown;
    • cooldown/re-entry integration with the shared account lifecycle.

    This issue does not own:

    • Antigravity-specific quota UI;
    • Cockpit Tools import;
    • provider-specific account-management screens;
    • user-defined account priority ordering beyond consuming that priority as a routing input.

    Related issues

    Acceptance criteria

    • Hard failover works when the active account becomes unusable because of rate limit/quota or authentication failure.
    • Ordinary healthy requests preserve session/account affinity.
    • Unknown-quota accounts are ranked deterministically and do not incorrectly outrank known-healthy accounts.
    • Temporarily limited accounts can re-enter the pool after cooldown/reset.
    • Optional quota-aware preemption does not become a dependency for correctness.
    • Provider integrations reuse the generic engine rather than implementing divergent failover logic.
    • Routing decisions are observable enough to explain why an account was selected, skipped, cooled down, or re-entered.
    • Regression coverage includes at least:
      • healthy account affinity;
      • 429 failover;
      • authentication-failure failover;
      • unknown-quota ranking;
      • cooldown and re-entry;
      • no healthy alternative available.
  12. lidge-jun commented on Aug 19, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 69 / 80

    현재 dev에는 Anthropic 옵트인 계정 풀(src/oauth/anthropic-routing.ts)처럼 프로바이더별 스티키 어피니티와 429 페일오버가 있습니다. 이 이슈가 소유한 일반 OAuth 풀 런타임은 그 파일이 아닙니다. 이슈 본문도 기존 구현을 복사해 일반 계약으로 삼지 말라고 합니다. #821 사용자 우선순위는 끝났고, Antigravity UX는 #1062/#1082로 분리됐습니다.

    이슈가 원하는 것은 재사용 엔진입니다. 세션 어피니티, 라우팅용 건강 분류, 할당 계정이 못 쓰일 때의 페일오버, 신뢰할 수 있는 쿼터 증거로 순위, unknown 쿼터의 결정적 처리, 쿨다운 재진입, 유계 설명입니다. 수락된 하드 페일오버는 429와 사용 불가 401, 그리고 프로바이더가 사용 불가로 분류한 403입니다. 일반 403으로 계정을 독극물 처리하면 안 됩니다.

    네이티브 신뢰성과 프로바이더 호환이 점수입니다. 건강한 대화는 계정에 붙고, 로드밸런싱으로 돌면 안 됩니다. unknown 쿼터는 건강하지만 미검증이며 0%나 죽은 계정이 아닙니다. 사전 95% 전환은 선택이며 하드 에러 경로와 같으면 안 됩니다. Windows/서비스에서 인플라이트 요청의 어피니티를 바꾸면 도구 턴이 계정을 갈아탑니다.

    위생적으로 Cockpit import(#1076)와 이 런타임을 섞지 마십시오. 가져오기는 자격 증명을 넣고, 이 이슈는 고릅니다. 프로바이더 UI가 선택 이유를 보여 주려면 엔진이 먼저 관측 가능한 결정을 내야 합니다.

    해결방안은 Anthropic 풀에서 정책을 일반화하되, 프로바이더 분류 훅만 남기고 계정 선택 코어를 한곳으로 모으는 것입니다. 하드 에러가 어피니티를 깨고, 쿼터 unknown은 후순위입니다. 패치는 추측하지 않으며, 현재 트리를 일반 OAuth 페일오버 완료로 보지 않습니다.

    이 댓글은 grok-bot이 작성했습니다

  13. nordz0r commented on Aug 29, 2026

    @nordz0r
    Contributor

    Adding concrete 2.35.0 evidence from a real 4-account Antigravity pool (Windows 10, npm @bitkyc08/opencodex 2.35.0) in support of implementing the accepted policy from this issue.

    State today — the pool exists, but every proactive control is Codex/Anthropic-only:

    ocx account list google-antigravity --quota — 4 OAuth accounts stored, but no quota probe and no priority data:

    PROVIDER            TYPE   ID                                PLAN/LABEL       PRIORITY  STATUS  QUOTA
    google-antigravity  oauth  37ea404f                          n***r@gmail.com  -                 -
    google-antigravity  oauth  de4a3658...                       m***a@gmail.com  -                 -
    google-antigravity  oauth  207353b7...                       r***c@gmail.com  -         active  -
    google-antigravity  oauth  a367fe95...                       s***9@gmail.com  -                 -
    
    > ocx account auto-switch google-antigravity status
    Error: auto-switch only applies to the openai Codex account pool
    
    > ocx account strategy google-antigravity
    Error: pool settings apply to the openai Codex pool and the anthropic pool, not "google-antigravity"
    

    Reactive 429 failover (oauthAccountFailover, #2568) does work and is genuinely useful, but it is strictly reactive: the daily-driver account keeps getting hammered until a hard 429, in-flight conversations are disrupted, and only then does rotation kick in. With 4 accounts the pool never spreads load proactively — there is no threshold, no strategy (quota/round-robin/fill-first), no priority tiers, no sticky limit for generic OAuth providers.

    Also note the pre-emptive leg of the policy accepted in this thread ("optional pre-emptive switch at ≥95% detected quota") is currently unreachable for Antigravity because there is no quota probe at all — QUOTA is - for every account (tracked separately in #1082). Until that lands, even the accepted policy can only operate on 429/401/403 signals.

    Happy to test the generic runtime against this 4-account pool once an implementation lands — good natural testbed (different plan types across the accounts).

  14. Ingwannu commented on Aug 29, 2026

    @Ingwannu
    Owner

    Confirmed against current dev 8df7051: your four-account evidence matches the implementation boundary.

    • Generic OAuth failover is present in src/oauth/generic-account-failover.ts and rotates eligible accounts reactively after upstream failures.
    • supportsPerAccountQuota currently admits only anthropic and kiro, so Google Antigravity has provider-level quota probing but no per-account quota evidence for proactive ranking.
    • The CLI auto-switch command is still Codex-only, and the shared strategy/sticky transport supports only Codex plus Anthropic. Google Antigravity is rejected before the management request.

    So this is not a configuration mistake and the issue should remain open. The safest implementation order is: first land the per-account Antigravity quota path tracked in #1082; then generalize the pool-settings API and CLI through a provider capability contract; finally let the generic OAuth selector consume that evidence for quota, round-robin, and fill-first behavior. That avoids creating a second Antigravity-only router and keeps unknown quota as unknown rather than treating it as zero capacity.

    Your four-account pool is a useful acceptance environment. The eventual regression should prove that proactive selection spreads new sessions without breaking existing affinity, while 401/403/429 recovery still walks the eligible roster and never exposes account identity or quota data across rows.

  15. lidge-jun commented on Sep 1, 2026

    @lidge-jun
    Owner

    Slice 1 landed on dev via #3214 (squash 356e495f5), following the order agreed above: per-account Antigravity quota went in with #3213, and this adds the pool-settings capability contract. Any generic OAuth provider (Antigravity, xAI, Cursor, Kimi, Copilot, Nous, …) now accepts strategy and autoSwitchThreshold through GET/PUT /api/oauth/accounts/pool and the ocx account strategy / ocx account auto-switch verbs, persisted on providers.<name>.oauthAccountFailover. Codex and Anthropic contracts are unchanged.

    What is deliberately still open here, for the next slice: the generic selector consuming those settings (quota / round-robin / fill-first, threshold-driven proactive switch), session affinity for generic pools, classified 401/403 hard failover, cooldown re-probe, and bounded selection reasons. Until that lands the stored settings are declared but inert, and the API says so ("inert": true). Keeping the issue open for that runtime slice.

  16. lidge-jun commented on Sep 3, 2026

    @lidge-jun
    Owner

    Closing in favor of #3375, which carries the remaining scope forward.

    Thank you @luwei1990 — this report on generic OAuth account-pool failover with session affinity and quota-aware selection was accurate, and part of it shipped.

    What landed. The first tranche landed: src/oauth/generic-account-failover.ts implements 429 cooldown, rotation, and quota-ranked initial selection (816f3a1).

    What is still missing. reusable session affinity, 401 / provider-classified 403 failover, the full health lifecycle, and decision observability.

    Rather than leave four issues open each describing one facet of the same unfinished lifecycle, the surviving scope is consolidated in #3375, which names this issue and credits you for the original analysis. Please follow and comment there; if you think the consolidation dropped something you asked for, say so on #3375 and it will be added.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    account-poolOAuth, credentials, Codex pool, quota, failover, plansenhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions