Skip to content

Codex pool session pins are lost on proxy restart, so round-robin moves running conversations to other accounts #8091

Closed

Description

@lidge-jun

Client or integration

Codex CLI

Area

Authentication and account pool

Summary

With the Codex account pool on round-robin (or any strategy with session pinning), a proxy restart moves most ongoing conversations to a different account. I expected a running conversation to stay on the account it was pinned to, as the Anthropic pool now does after #7813 / #7828.

Two things in dev cause it:

  1. threadAccountMap (conversation key -> scope -> account) and conversationStateIssuerMap (binding key -> account that minted carried encrypted state) in src/codex/routing/thread-affinity.ts live only in memory, so a restart drops every pin.
  2. Even a persisted map would not match after a restart: the key it is indexed by comes from codexConversationKeyFor() in src/codex/lineage.ts, an HMAC under CODEX_LINEAGE_KEY = randomBytes(32) generated at module load, so the same conversation derives a different key in the next process.

Effects after a restart:

  • The next turn of each running conversation is placed by the strategy cursor, so under round-robin most conversations land on another account and their prompt cache goes cold.
  • The issuer record that triggers the account-change scrub is gone too, so carried state (previous_response_id, encrypted reasoning) minted by the old account can be sent to the new one and fail with invalid_encrypted_content.

Reproduction

  1. Add two or more Codex pool accounts and set the pool strategy to round-robin.
  2. Start a Codex CLI session and send a few turns; note the serving account in the request log.
  3. Restart OpenCodex (ocx restart) and send the next turn of the same session.
  4. The turn is usually served by a different account; the earlier pin is gone.

Version

2.97.0 (dev, commit 6f1d3e6)

Operating system

macOS 15 (Apple silicon); the code path is platform independent.

Provider and model

openai / Codex pool (ChatGPT accounts)

Logs or error output

No error for the account move itself; it shows up as a changed account label within one conversation after the restart. When carried encrypted state reaches the new account the upstream answers invalid_encrypted_content.

Screenshots and supporting files

No response

Redacted configuration

{ "accountPoolStrategy": "round-robin", "codexAccounts": ["<two or more pool accounts>"] }

Checks

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

Activity

  1. github-actions commented on Nov 2, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: English.

  2. lidge-jun commented on Nov 2, 2026

    @lidge-jun
    OwnerAuthor

    Fixed on dev by #8098 (merge commit 5cfd0ac11).

    Codex pool session pins (thread affinity) and conversation-state issuers are now saved in a private SQLite store in the config home and restored after a proxy restart. Ongoing conversations stay on their account, and the prompt cache stays warm. Rows are keyed by an HMAC under a separate secret, while the request-log and lineage key stays process-local. Rows expire after 24 hours idle, and their number and size are capped. On restore, a pin whose account was deleted, re-logged, paused or excluded falls back to normal selection. Pins tied to a caller's own bearer (#8096) are never saved.

    It will ship in the next release.

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