Client or integration
Codex CLI (the same Pool state is visible in the OpenCodex dashboard)
Area
Authentication and account pool
Summary
On OpenCodex 2.39.0, quota routing can deterministically override a healthy manually selected main account and persistently switch to a pool account whose weekly quota is already known to be 100% exhausted.
This is not the short-only/unknown-score case fixed by #3029 / #3110. In this reproduction:
- main account:
weeklyPercent: 16
- stored pool account:
weeklyPercent: 100
- strategy:
quota
- auto-switch threshold:
90
- both account selection priorities:
0
- both accounts are confirmed entitled to
gpt-5.6-sol
After manually selecting main, OpenCodex reports activeId=__main__ and persists activeCodexAccountPinned=__main__. The first minimal request removes that pin, persists the exhausted stored account as active, routes the request to it, and returns a synthetic 502 with The usage limit has been reached.
I expected the known-100% account to be excluded from quota selection and the healthy 16% main account to remain selected.
Reproduction
-
Configure the native main Codex login plus one stored Codex pool account.
-
Use equal selection order for both accounts (0 / normal).
-
Configure quota routing:
ocx account strategy openai quota
ocx account auto-switch openai threshold 90
-
Ensure the stored account is resumed, then refresh quota:
ocx account resume openai <pool-account-id>
ocx account refresh openai --json
-
Confirm the response shows:
__main__.quota.weeklyPercent = 16
<pool>.quota.weeklyPercent = 100
- neither account paused
- both priorities
0
-
Select main:
ocx account use openai main --json
-
Confirm before the request:
ocx account current openai --json reports activeId: __main__
ocx config show reports both activeCodexAccountPinned: __main__ and activeCodexAccountId: __main__
-
Send one minimal request:
codex exec --skip-git-repo-check --sandbox read-only \
--model openai/gpt-5.6-sol 'Reply with exactly: ROUTE_PROBE_OK'
-
Observe five reconnects followed by adapter_eof / incomplete response.
-
Read state again:
ocx account current openai --json now reports the exhausted stored account as active
activeCodexAccountPinned is gone
activeCodexAccountId is persisted as the exhausted stored account
-
ocx observe logs --limit 1 --json shows the request was served by the stored account and failed with the quota message below.
The reproduction was repeated after resetting both priorities to 0; the same result occurs. Pausing the exhausted account and selecting main makes the same minimal request succeed immediately.
Expected behavior
With strategy=quota, threshold 90, a known 16% main account, and a known 100% stored account:
- the stored account should not be selected;
- a manual main pin with headroom should not be replaced by the exhausted account;
- existing and new requests should route through main;
- if an in-stream usage-limit terminal cannot safely retry the same request, it should at least make the exhausted account ineligible for the next request.
Actual behavior
The first request after selecting main automatically clears the healthy main pin, persists the known-exhausted stored account as active, and routes to it. Subsequent requests remain locked to that account and repeatedly fail.
Version
OpenCodex 2.39.0 (npm latest at reproduction time)
Codex CLI 0.150.1
Bundled Bun 1.4.0
Operating system
macOS 26.6.2 (25G83), Apple Silicon arm64
Provider and model
OpenAI Codex login account pool / gpt-5.6-sol
Logs or error output
Before request:
strategy: quota
auto-switch: on (threshold 90%)
priorities: main=0, pool=0
quota: main weekly=16%, pool weekly=100%
activeId: __main__
activeCodexAccountPinned: __main__
activeCodexAccountId: __main__
Request result:
Reconnecting... 1/5
...
Reconnecting... 5/5
stream disconnected before completion: Incomplete response returned, reason: adapter_eof
OpenCodex request log:
status: 502
accountLogLabel: <stored-pool-account>
model: gpt-5.6-sol
errorCode: upstream_server_error
terminalStatus: incomplete
upstreamError: The usage limit has been reached
After request:
activeId: <stored-pool-account>
activeCodexAccountPinned: absent
activeCodexAccountId: <stored-pool-account>
Network and auth checks were healthy during reproduction:
ocx health
# Proxy healthy
ocx doctor
# WHAM status=200 authenticated
# OAuth reliability checks OK
# model catalog current
Redacted configuration
{
"providers": {
"openai": {
"adapter": "openai-responses",
"codexAccountMode": "pool",
"authMode": "forward"
}
},
"accountPoolStrategy": "quota",
"autoSwitchThreshold": 90,
"upstreamFailoverThreshold": 5,
"codexAccountPickerEnabled": false,
"websockets": false
}
Relation to #3029
#3029 was closed by #3110 / commit 42ad9c44d, which makes a live short-only 100% window score as exhausted instead of unknown. This reproduction is on 2.39.0 after that fix and has a known long-window value: the stored account has weeklyPercent=100 and main has weeklyPercent=16. Therefore the exhausted account should already score 100 through the ordinary long-window path.
The in-stream usage terminal is still surfaced as synthetic 502 rather than pre-stream 429/402, so same-request quota retry does not run. However, the more surprising part is that the routing decision actively moves away from a healthy pinned main account and persists the known-100% stored account before producing that failure.
Workaround
Disable auto-switch, pause the exhausted account, and manually select main:
ocx account auto-switch openai off
ocx account pause openai <pool-account-id>
ocx account use openai main
This keeps the stored account and credentials intact and makes the minimal request succeed.
Checks
Client or integration
Codex CLI (the same Pool state is visible in the OpenCodex dashboard)
Area
Authentication and account pool
Summary
On OpenCodex 2.39.0,
quotarouting can deterministically override a healthy manually selected main account and persistently switch to a pool account whose weekly quota is already known to be 100% exhausted.This is not the short-only/unknown-score case fixed by #3029 / #3110. In this reproduction:
weeklyPercent: 16weeklyPercent: 100quota900gpt-5.6-solAfter manually selecting main, OpenCodex reports
activeId=__main__and persistsactiveCodexAccountPinned=__main__. The first minimal request removes that pin, persists the exhausted stored account as active, routes the request to it, and returns a synthetic 502 withThe usage limit has been reached.I expected the known-100% account to be excluded from quota selection and the healthy 16% main account to remain selected.
Reproduction
Configure the native main Codex login plus one stored Codex pool account.
Use equal selection order for both accounts (
0 / normal).Configure quota routing:
Ensure the stored account is resumed, then refresh quota:
Confirm the response shows:
__main__.quota.weeklyPercent = 16<pool>.quota.weeklyPercent = 1000Select main:
Confirm before the request:
ocx account current openai --jsonreportsactiveId: __main__ocx config showreports bothactiveCodexAccountPinned: __main__andactiveCodexAccountId: __main__Send one minimal request:
Observe five reconnects followed by
adapter_eof/ incomplete response.Read state again:
ocx account current openai --jsonnow reports the exhausted stored account as activeactiveCodexAccountPinnedis goneactiveCodexAccountIdis persisted as the exhausted stored accountocx observe logs --limit 1 --jsonshows the request was served by the stored account and failed with the quota message below.The reproduction was repeated after resetting both priorities to
0; the same result occurs. Pausing the exhausted account and selecting main makes the same minimal request succeed immediately.Expected behavior
With
strategy=quota, threshold90, a known 16% main account, and a known 100% stored account:Actual behavior
The first request after selecting main automatically clears the healthy main pin, persists the known-exhausted stored account as active, and routes to it. Subsequent requests remain locked to that account and repeatedly fail.
Version
OpenCodex 2.39.0 (
npm latestat reproduction time)Codex CLI 0.150.1
Bundled Bun 1.4.0
Operating system
macOS 26.6.2 (25G83), Apple Silicon arm64
Provider and model
OpenAI Codex login account pool /
gpt-5.6-solLogs or error output
Network and auth checks were healthy during reproduction:
Redacted configuration
{ "providers": { "openai": { "adapter": "openai-responses", "codexAccountMode": "pool", "authMode": "forward" } }, "accountPoolStrategy": "quota", "autoSwitchThreshold": 90, "upstreamFailoverThreshold": 5, "codexAccountPickerEnabled": false, "websockets": false }Relation to #3029
#3029 was closed by #3110 / commit
42ad9c44d, which makes a live short-only 100% window score as exhausted instead of unknown. This reproduction is on 2.39.0 after that fix and has a known long-window value: the stored account hasweeklyPercent=100and main hasweeklyPercent=16. Therefore the exhausted account should already score 100 through the ordinary long-window path.The in-stream usage terminal is still surfaced as synthetic 502 rather than pre-stream 429/402, so same-request quota retry does not run. However, the more surprising part is that the routing decision actively moves away from a healthy pinned main account and persists the known-100% stored account before producing that failure.
Workaround
Disable auto-switch, pause the exhausted account, and manually select main:
This keeps the stored account and credentials intact and makes the minimal request succeed.
Checks