You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Codex pool session pins are lost on proxy restart, so round-robin moves running conversations to other accounts #8091
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:
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.
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
Add two or more Codex pool accounts and set the pool strategy to round-robin.
Start a Codex CLI session and send a few turns; note the serving account in the request log.
Restart OpenCodex (ocx restart) and send the next turn of the same session.
The turn is usually served by a different account; the earlier pin is gone.
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.
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.
Activity
github-actions commentedon Nov 2, 2026
Automated translation bookkeeping — detected language: English.
lidge-jun commentedon Nov 2, 2026
Fixed on
devby #8098 (merge commit5cfd0ac11).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.