Skip to content

Pool routing never selects the main login automatically when the request carries that login's own bearer #5019

Description

@FredAmartey

Client or integration

Codex App

Area

Authentication and account pool

Summary

In Pool mode with the Codex app signed in as the main login and one stored account added through ocx account login openai, automatic placement never picks the main login. Every unpinned request goes to the stored account, whatever the usage numbers, the strategy or codexAccountPriorities say. Main only serves when it is pinned, or when no stored account can be selected.

The cause is in resolveCodexAuthContext (src/codex/auth-context.ts): nativeMainReadsForbidden is set whenever requestScopedMainCredential is true, and selection receives isMainAccountTokenLive: () => preserveRequestOwnedMainPin. That callback is true only when main is the effective pin with headroom, so for an unpinned request codexAccountUnusableReason("__main__") returns main_credential_unavailable, getEligiblePoolAccounts never lists main, and the strategy only compares the stored accounts. Main is reached afterwards through resolveCallerOwnedMainContext, but only as the fallback when nothing else is selectable.

The fence itself makes sense: a request that carries its own main bearer must not read or reconcile the stored auth.json. But that same bearer is exactly what would be forwarded if main were selected, so excluding main from the comparison discards the usage, priority and reset ordering the operator configured. With one stored sibling the pool degrades to "stored account until it cannot serve, then main". #3996 (fixed) made the caller-owned fallback reachable in more cases; this is about main taking part in the ordering before that fallback.

Expected: for a request carrying a forwardable main bearer, main takes part in selection as a candidate served from that bearer, ordered by the same usage, priority and strategy as the stored accounts, without touching the stored main credential.

Reproduction

  1. Canonical openai provider in Pool mode (codexAccountMode: "pool", authMode: "forward"). Codex app signed in as the main login. Add one stored account: ocx account login openai --device.
  2. Make the numbers favour main: main at lower weekly usage than the stored account (here 46% against 75%). Clear any pin: PUT /api/codex-auth/active with {"accountId": null}; GET /api/codex-auth/active then shows activeCodexAccountId and pinnedAccountId both null.
  3. Send fresh turns from the app. I used codex exec --ephemeral -c 'mcp_servers={}' "Reply with the single word yes." with a different word each time so every turn is a new conversation, then read accountLogLabel on the newest row of GET /api/logs?limit=1.
  4. Observed: every turn is served by the stored account. Same result with accountPoolStrategy quota and reset-first, with ocx account priority openai main first, on gpt-6-astra and on gpt-5.5.
  5. Control: PUT /api/codex-auth/active {"accountId": "__main__"} and the next turn is served by main. Pause the stored account and the next unpinned turn is served by main. GET /api/codex-auth/accounts?refresh=1 shows main with healthLabel: "Healthy", quotaRefresh: {"status": "ok"}, needsReauth: false, so it is not a credential or health problem.

Version

2.58.0; the same path is present in current dev (src/codex/auth-context.ts, nativeMainReadsForbidden).

Operating system

macOS 26.0 (Darwin 25.3)

Provider and model

openai, canonical ChatGPT backend; gpt-6-astra and gpt-5.5

Logs or error output

No error is produced. GET /api/logs?limit=1 after each unpinned turn carries the stored account's accountLogLabel; after a pinned-main turn the field is absent, which is how main is reported.

Screenshots and supporting files

No response

Redacted configuration

"openai": {
  "adapter": "openai-responses",
  "baseUrl": "https://chatgpt.com/backend-api/codex",
  "authMode": "forward",
  "codexAccountMode": "pool"
}

One stored pool account. accountPoolStrategy tried as quota and reset-first, autoSwitchThreshold 80, codexAccountPriorities with main at +2.

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 working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions