Skip to content

Subagent fallback can reuse the wrong Pool account across model quota scopes #2509

Description

@luvs01

Client or integration

Codex App and Codex CLI using OpenCodex subagent model fallback.

Area

Proxy and routing.

Summary

Subagent fallback previews one Pool account for the initially resolved model and reuses that account while evaluating every later fallback candidate. Native models can have independent quota scopes, so the account preferred for a shared-quota model is not necessarily the account that Pool selection would choose for a Spark-style scoped candidate.

This can make a healthy fallback candidate look unavailable: OpenCodex checks the candidate's scoped cooldown, quota, or model eligibility against the stale account and skips it even though another Pool account is usable for that candidate. The final authentication step is already model-aware, but selection can reject the candidate before final authentication runs.

The fix should be delivered as small review units rather than one cross-cutting change:

  1. Preview a Pool account independently for each fallback candidate's resolved model scope. Keep preview side-effect-free and leave fixed account selectors fixed.
  2. Apply account-gated model eligibility at the fallback boundary while preserving lifecycle exclusions for native-main credentials.
  3. Reserve a due model-scoped quota probe atomically during live selection, transfer that exact reservation to final authentication, and release it on every pre-upstream failure.
  4. Resolve account-gated entitlement data lazily and keep roster discovery behind the ChatGPT host circuit, including half-open ownership.
  5. Re-run the same selection contract after encrypted task recovery with a fresh lifecycle admission.
  6. Preserve public route identity when a fallback changes only an account selector while the physical model slug remains the same.

The first unit is intentionally independent of authentication internals. Later units that touch credential or auth ownership should receive their own security sponsorship and review.

This is related to, but distinct from, #590. That issue fixed same-account combo behavior after a reset-derived 429; this issue concerns subagent fallback planning reusing one account preview across different candidate quota scopes.

Reproduction

  1. Configure canonical OpenAI Codex-login Pool mode with two usable accounts, pool-a and pool-b.
  2. Configure a subagent fallback chain whose initial native model uses the shared quota scope and whose next native candidate uses an independent model scope.
  3. Arrange current Pool state so the shared scope previews pool-a, while the independent candidate scope should select pool-b (for example, pool-a has a model-scoped cooldown and pool-b is healthy for that candidate).
  4. Make the initial model unavailable so fallback evaluation reaches the independent candidate.
  5. Send a thread_spawn request.
  6. Observe that current selection reuses the initial pool-a preview, evaluates the independent candidate against pool-a, and skips it or advances farther in the chain.

Expected behavior:

  • each unqualified Pool candidate is previewed under codexQuotaScopeForModel(candidate);
  • preview does not move the account cursor, bind affinity, refresh credentials, or acquire a quota probe;
  • fixed account selectors are evaluated only against their mapped account;
  • final authentication remains authoritative and may still reject a candidate if current live state changed after preview.

Version

@bitkyc08/opencodex 2.32.0; confirmed in source at dev@bb89eafbe2e144fa44534437a6cc163fec75381b.

Operating system

Windows 11 Pro 10.0.26200. The state-machine defect is platform-independent.

Provider and model

Canonical openai Codex-login provider in Pool mode; a shared-quota native primary followed by an independently scoped native fallback candidate.

Logs or error output

expected: independently scoped candidate is evaluated with pool-b and remains selectable
actual:   candidate is evaluated with the initial pool-a preview and is skipped before final auth

Screenshots and supporting files

No screenshot is required. The behavior can be reproduced deterministically with focused routing and Responses-handler tests.

Redacted configuration

{
  "providers": {
    "openai": {
      "adapter": "openai-responses",
      "baseUrl": "https://chatgpt.com/backend-api/codex",
      "codexAccountMode": "pool"
    }
  },
  "activeCodexAccountId": "pool-a",
  "codexAccounts": [
    { "id": "pool-a" },
    { "id": "pool-b" }
  ],
  "subagentModelFallback": [
    "gpt-5.3-codex-spark",
    "provider/fallback-model"
  ]
}

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

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, plansbugSomething isn't workingcatalogModel catalog, slugs, visibility, routed entrieslanded-via-maintainerOriginal PR closed after landing via a maintainer merge train

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions