Repository navigation
[Bug]: v2.57.0 still selects Free account for Sol/Astra after roster refresh #4906
Description
Client or integration
OpenCodex Codex account pool on Windows
Area
Codex account entitlement-aware routing
Summary
This remains reproducible after the fix landed in PR #4797 / OpenCodex v2.57.0.
The pool contains multiple Codex OAuth accounts: one main Plus account, three Free accounts, and one additional Plus account. After running the account quota refresh and model catalog sync, OpenCodex still selected a Free account for both gpt-5.6-sol and gpt-6-astra, then returned the upstream unsupported-model 400 instead of selecting an entitled Plus account or retrying on one.
The previous issue was #4768. This report is a follow-up verification for the behavior after #4797 landed.
Expected behavior
When a requested model has account-specific availability, the pool should not select an account whose authenticated roster confirms that it cannot serve the model. If no confirmed denial is available, the implementation should still provide a bounded alternate-account retry for the exact pre-stream unsupported-model 400 when another account exists.
Reproduction
- Install OpenCodex v2.57.0 on Windows 11 24H2.
- Configure a pool containing Plus and Free Codex OAuth accounts.
- Run
opencodex account refresh openai. - Run
opencodex sync --restart-codex. - Start a new request using
gpt-5.6-sol. - Repeat using
gpt-6-astra. - Inspect
opencodex logs --jsonl.
Version
OpenCodex 2.57.0 (@bitkyc08/opencodex@2.57.0).
Operating system
Windows 11 24H2.
Provider and model
Codex / ChatGPT OAuth account pool.
Models affected:
gpt-5.6-solgpt-6-astra
Observed result
For gpt-5.6-sol:
status: 400
provider: openai-pd56bf5
accountLogLabel: pd56bf5
upstreamError: The 'gpt-5.6-sol' model is not supported when using Codex with a ChatGPT account.
attempts: 1
recoveryKinds: []
moveReasons: ["healthy"]
For gpt-6-astra:
status: 400
provider: openai-pd56bf5
accountLogLabel: pd56bf5
upstreamError: The 'gpt-6-astra' model is not supported when using Codex with a ChatGPT account.
attempts: 1
recoveryKinds: []
moveReasons: ["healthy"]
The selected account label corresponds to the Free account shown by opencodex account list openai as selected.
Additional context
The account quota refresh completed successfully and returned quota data for all five accounts. The model catalog sync also completed successfully and appended 83 models. However, the refresh did not produce usable per-account model-roster denial evidence for Sol/Astra, or the denial evidence is not being consumed by the initial native Codex selection path.
The v2.57.0 source contains ENTITLEMENT_PREFERRED_NATIVE_OPENAI_MODELS and cachedDeniedCodexAccountIdsForModel, but the live behavior above still routes to the Free account and performs no alternate attempt.
A local WebSocket warning also appeared:
failed to connect to websocket: HTTP error: 426 Upgrade Required, url: ws://127.0.0.1:10100/v1/responses
The relevant terminal failure remains the account-specific unsupported-model 400.
Requested fix
Please investigate and fix the remaining gap so that:
- Full authenticated
/codex/modelsroster refresh is available for every pool account, not only quota refresh. - Confirmed per-account model denial is persisted and consumed by the initial native Codex account selector for Sol/Terra/Luna/Astra.
- The exact unsupported-model 400 reliably triggers a bounded alternate-account retry when the request is replayable and another account is available.
- Add an integration/regression test covering a Plus + Free pool for
gpt-5.6-solandgpt-6-astra. - Make the logs expose a redacted routing reason such as
account-model-deniedorentitlement-unknown.
Please relate this to the original issue and verify against a current Windows installation.
Checks
- I searched existing issues and related PRs.
- I removed account emails, tokens, API keys, and private credentials.
Activity
github-actions commentedon Sep 17, 2026
Issue reopened
The report now contains the information required by the automated check. Thanks for updating it.
lidge-jun commentedon Sep 17, 2026
리뷰 · 우선순위 62 / 80
이 이슈는 Windows에서 Codex OAuth 풀(Plus+Free)을 쓴 뒤에도
gpt-5.6-sol/gpt-6-astra요청이 Free 계정(openai-pd56bf5)으로 가서 upstream unsupported-model 400을 맞고,attempts: 1·recoveryKinds: []·moveReasons: ["healthy"]만 남는다는 후속 보고다. 원본은#4768이고,#4797이 2.57.0에 들어간 뒤에도 같은 증상이 남는다는 검증이다.지금
devHEAD41f1832f6(패키지 2.58.0)에는#4797(f35fbe8ef)가 이미 있다.src/codex/routing/selection.ts의withoutModelDeniedAccounts/preferModelEntitledAccount주석이 바로 이 모양을 설명한다. ACTIVE Free 커서가 eligible 목록을 안 거치면 Sol/Astra가 계속 Free로 가고 400만 반복한다는 것, 그래서 roster denial이 있으면 같은 요청 안에서 entitled 계정으로 한 번 고친다는 것이다. 또한 tip에는 2.57.0 트리에 없던#4883(b60a6283c, pool eligibility를 caller-owned preview read fence 안에서 닫기)도 있다. 보고 버전은 명시적으로 2.57.0이므로, tip/2.58.0과의 간격부터 갈라야 한다.로그가 가리키는 핵심은 「roster가 Free를 이미 denial했는데도 선택됐다」인지, 「denial 증거가 비어 있어서 ordering이 no-op이고 alternate-account retry도 안 탔다」인지다.
preferModelEntitledAccount는deniedModelAccountIds가 없거나 active가 denial 집합에 없으면 항등함수다. 핀된 active도 그대로 둔다. 그리고 주석대로 사전 거절이 아니라 ordering + 400 후 bounded retry가 안전망이다. 그런데 이 로그는attempts: 1이라 retry 안전망도 안 탄 것처럼 보인다. 그래서 tip에서도 구멍일 수 있고, 2.57.0에서 roster/refresh 타이밍·fence 문제일 수도 있다.다음 확인이 없으면 코드 패치를 바로 단정하기 어렵다. (1) 같은 풀로 2.58.0/
dev에서 재현되는지. (2) 그때deniedModelAccountIds/roster에 Free가 sol·astra denial로 잡히는지. (3) Free가 manual pin인지. (4) unsupported-model 400 분류가 alternate-account retry 조건과 문자열/코드가 일치하는지. types.ts/config.ts 분할·preview deploy와는 무관하다.우선순위는 중상이다. 계정 풀+게이티드 모델은 체감이 크고,
#4797후에도 남는다면 tip 회귀/잔여 구멍 가능성이 있다. 다만 보고 트리가 2.57.0이므로 먼저 tip에서 재현을 가르는 게 맞다.로그 attempts:1 / recoveryKinds:[] - alternate-account retry 안전망이 안 탄 신호. tip에서 분류·트리거를 대조해야 한다.
src/codex/routing/selection.ts preferModelEntitledAccount - denial 증거가 있을 때만 Free 커서를 보정. 증거 없으면 항등.
#4797 on 2.57.0 / #4883 on tip only - 보고 버전과 tip 사이에 fence 관련 추가 수정이 있다.
account refresh + sync 후 재현 - roster가 denial을 채웠는지 로그/덤프가 더 필요하다.
메인테이너의 판단이 필요한 지점
너의 추천
먼저 같은 풀로 2.58.0/
dev재현을 요청. tip에서도 attempts:1이면deniedModelAccountIds전달 경로와 unsupported-model retry 분류를 추적하는 수정 PR 후보. tip에서 사라지면 2.57.0 한정으로 안내 후 클로즈.이 댓글은 grok-bot이 작성했습니다
fix(codex): decide flagship model availability by roster and refusal …
lidge-jun commentedon Sep 17, 2026
Fixed on
devby #4921, squashed as987456350b57b5b060d445015598659f9659ab87.The ordering rules were already correct; they had no evidence to act on.
withoutModelDeniedAccountsandpreferModelEntitledAccountboth readcachedDeniedCodexAccountIdsForModel, which is cache-only, and the roster cache expires five minutes after a catalog sync fills it. Nothing on the flagship request path refilled it, so the denial set wasundefinedfor most requests, both rules degraded to the identity function, and the pool selected on quota alone — which is the Free account.The refusal itself was the missing evidence. A 400 whose body is exactly
The '<model>' model is not supported when using Codex with a ChatGPT account.is authenticated, account-specific and model-specific, and it was being spent on one retry and discarded.src/codex/observed-model-denials.tsnow records it per (account, model) with six-hour retention,cachedDeniedCodexAccountIdsForModelunions it with the roster-derived denials, and the pair is cleared when that account successfully serves that model.One detail worth recording because it silently disabled two existing recovery paths: detection used to rebuild the sentence from
route.modelId, butapplyCodexAccountGatedWireNormalizationrewrites Daybreak togpt-5.6-solbefore dispatch, so upstream named Sol while the route still said Daybreak. The comparison never matched, which turned off both the alternate-account retry and the eight-rung same-account ladder for exactly that model. It now reads the model upstream actually named.This stays evidence for ordering rather than a gate: a denied account is still restored when filtering would empty the candidate list, the active account is still returned unchanged when no entitled alternative exists, and an operator's manual pin is still exempt.
Shipping in the next release off
dev.fix(codex): keep main-pool denial evidence and fence stale writes bef…
fix: stop one account failure from taking the whole proxy out of serv…