Skip to content

[bug] org-sourced tokens.account_id breaks every request on Codex CLI >= 0.156.0 ("selected workspace missing from routing discovery") #700

Description

@justinbuzzni

Summary

Codex CLI 0.156.0 introduced a "workspace routing discovery" step. It treats tokens.account_id in ~/.codex/auth.json as the selected workspace and verifies it against the workspaces that discovery returns for the ChatGPT account derived from the token claims. codex-multi-auth writes the org id into that field for accounts whose accountIdSource is org, so discovery cannot find it and every request fails — regardless of which model is requested. Codex 0.154.0 is unaffected, because it did not perform this lookup.

This is not a model-specific or account-specific problem: on my machine 7 of 8 managed accounts are affected, and the only unaffected one is the single account whose stored accountId happens to equal its token's chatgpt_account_id claim.

I want to flag up front that writing the org id looks deliberate in the current design — resolveRequestAccountId is documented as "Preserves explicit org/manual selections; only token/id_token sources auto-follow token changes." So this is a semantics change on the Codex CLI side that the current design predates, not a simple coding slip. That is also why I am not sending a patch blindly: the right fix depends on how you want workspace selection expressed under the new contract (see "Possible direction" below).

Reproduction

  1. Add a ChatGPT account and select an org/team workspace, so the stored account has accountIdSource: "org" and accountId: "org-…".
  2. Let codex-multi-auth sync the selection into ~/.codex/auth.json (the written file contains codexMultiAuthSyncVersion and tokens.account_id = "org-…").
  3. Install Codex CLI 0.156.0 or 0.157.0-alpha.10.
  4. Run any request against the official binary, e.g. codex exec --skip-git-repo-check "reply with exactly: OK".

Expected Behavior

The request succeeds, as it does on Codex CLI 0.154.0 with the exact same auth.json.

Actual Behavior

warning: Falling back from WebSockets to HTTPS transport. selected workspace missing from routing discovery
ERROR: Reconnecting... 1/5
...
ERROR: selected workspace missing from routing discovery

Reproduced with the default model and with explicitly selected models; the model is irrelevant. Running the older 0.154.0 binary directly against the same auth.json succeeds immediately.

Evidence

Comparing the codex-multi-auth-written auth.json with one written by a native codex login (values redacted, shapes preserved):

tokens.account_id chatgpt_account_id claim in tokens.id_token result
codex-multi-auth synced org-XXXXXXXX… (org id) <uuid> fails
native codex login <uuid> <uuid> (same value) works

Decoding each stored account's accessToken claims and comparing them with the stored accountId:

  • 7 of 8 accounts: stored accountId is org-…, token claim chatgpt_account_id is a UUID → mismatch
  • 1 of 8 accounts: accountIdSource: "token", both values identical → match, and this is the only account that works

Relevant code on main (2.16.0):

  • lib/codex-cli/writer.ts:454nextTokens.account_id = selection.accountId.trim(); writes the stored id verbatim, org ids included.
  • lib/auth/token-utils.ts:361-384shouldUpdateAccountIdFromToken / resolveRequestAccountId intentionally keep org-sourced ids instead of following the token claim.

What I could not verify: I wanted to run the decisive single-variable A/B (same credentials, only tokens.account_id swapped from the org id to the token's chatgpt_account_id). The native codex login I ran to recover my machine invalidated the previously active account's refresh token (token_revoked / refresh_token_invalidated), so the backed-up credentials were dead before I could run it, and the stored accounts carry no id_token to rebuild a valid auth.json from. So the causal link is inferred from the structural correlation plus the error semantics, not from a controlled experiment. If you can run that swap on a live org-selected account, it should settle it in one step.

Possible direction (for discussion, not a proposal I have validated)

Codex 0.157.0-alpha.10 ships a forced_chatgpt_workspace_id config.toml key, and its own validation message shows UUID-shaped workspace ids:

forced_chatgpt_workspace_id must be a single workspace ID string or a TOML list of strings; comma-separated strings are not supported. Use `forced_chatgpt_workspace_id = ["123e4567-e89b-42d3-a456-426614174000", …]` instead.

That suggests the new contract splits the two concerns that tokens.account_id used to carry:

  • tokens.account_id → the ChatGPT account id (must match the token claim)
  • workspace/org pinning → forced_chatgpt_workspace_id

If that reading is right, preserving today's behavior under new Codex versions would mean writing the claim-derived account id into auth.json and expressing the org selection through the workspace key instead. Two things I do not know and did not want to guess at:

  1. whether forced_chatgpt_workspace_id accepts org-… ids or only the UUID workspace ids, and
  2. whether the change needs to be conditional on the detected Codex CLI version, since 0.154.0 and earlier appear to rely on the old meaning.

Happy to prepare a PR once you indicate the direction you prefer.

Environment

  • codex --version: codex-cli 0.157.0-alpha.10 (also reproduced on 0.156.0; 0.154.0 unaffected)
  • codex-multi-auth: failure observed on 2.15.0; code references above are from main / 2.16.0
  • codex-multi-auth status / check / report --json / doctor --json: intentionally omitted — my machine is currently on a native codex login that works, and I did not want to run commands that may re-sync auth.json and break it again. I can collect them on request in a throwaway CODEX_HOME.
  • OS: macOS 26.5.1 (arm64)
  • Node.js: v22.22.3

Compliance Confirmation

  • I am using this project for personal development workflows.
  • This report does not request policy bypasses or prohibited usage.
  • I removed any secrets/tokens from this report.

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions