Repository navigation
[Feature]: keep Free-tier ChatGPT accounts out of Codex pool selection #4211
Description
Area
Authentication and account pool
What are you trying to accomplish?
I run a Codex account pool built from several paid ChatGPT accounts. When one subscription lapses and the account is downgraded to Free, I want pool selection to stop handing it production traffic, instead of discovering the downgrade from failed or degraded requests.
Reported on X by @LjranceLi on 2026-09-10: one of their accounts expired without being renewed, was downgraded to Free, and they asked for "an option to only select non-free accounts" for GPT subscribers (https://x.com/LjranceLi/status/2097934784895058054).
What prevents this today?
Pool eligibility never looks at the plan tier. In src/codex/account-usability.ts at dev 6d3ad12e3 (2.51.0), isCodexAccountUsable() decides selection from an optional modelEligibleAccountIds set for account-gated models, the main-account hard lock / native-main traffic block / legacy pool sentinel, isAccountNeedsReauth(), pool membership, a stored credential, deletedAt, and codexValidationPending. The plan is not an input to any branch.
The tier is already known locally:
src/codex/plan-from-token.tspersists the JWTchatgpt_plan_typeontocodexAccounts[].planwhen it contradicts the stored value.src/codex/plan.tsclassifies plan keys and already treatsgoandfreespecially (isThirtyDayOnlyCodexPlan).src/codex/main-account.tsdocuments the main-account plan label (plus,go,free,team).
The only exclusion mechanism is manual and untyped: src/codex/account-pause.ts writes pausedCodexAccountIds, which an operator has to set by hand after they have already diagnosed the failure.
What should OpenCodex do?
- Let an operator declare a plan policy for pool selection, so a Free (or otherwise below-threshold) account is skipped for ordinary routing.
- When a refresh observes that an account's plan dropped below the policy, stop selecting it and record a reason that names the plan change, instead of leaving it in rotation to fail per request.
- Show that reason where the operator already looks: the account card in the dashboard and the account line in CLI status output, with the plan label visible.
- Keep an explicit, operator-chosen route to that account working. This is a selection policy, not a hard block.
Example usage or interface
# opt in: never auto-select a downgraded account
ocx config set codexPool.excludedPlans '["free"]'
# or, expressed as a floor
ocx config set codexPool.minimumPlan plusDashboard, Providers -> Codex account card:
account: work@example.com plan: free state: not selected (plan below codexPool.minimumPlan=plus)
Alternatives or workarounds
- Manually pause the account (
pausedCodexAccountIds). That works only after the operator has already diagnosed which account regressed. - Delete and re-add the account after renewal. Same diagnosis cost, and it discards affinity and quota history.
Additional context
- Newly created ChatGPT Free account fails Codex account warmup with HTTP 404 #4126 (newly created ChatGPT Free account fails Codex warmup with HTTP 404) and PR fix(codex): retry warmup with gpt-5.6-luna on 400 and 404 for ChatGPT Free accounts (#4126) #4188 cover the warmup side of the same tier problem; this issue is about selection policy, not warmup repair.
- feat(codex): add per-account auto-switch thresholds #4020 (per-account auto-switch thresholds) and [Feature]: complete the OAuth account-pool lifecycle — session affinity, 401/403 rotation, pool health, and stable reset-credit identity #3375 (OAuth account-pool lifecycle) are adjacent policy work.
Checks
- I searched existing issues and documentation.
- This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
- I removed secrets and personal data.
Activity
lidge-jun commentedon Sep 10, 2026
리뷰 · 우선순위 68 / 80
이 이슈는 Codex 계정 풀에서 ChatGPT 구독이 만료되어 Free로 내려간 계정을, 실패한 요청으로 알아차리기 전에 자동 선택에서 빼고 싶다는 기능 요청이다. @LjranceLi가 X에서 “non-free만 고르는 옵션”을 요청한 흐름과 같다. 지금은
src/codex/account-usability.ts의isCodexAccountUsable이 모델 자격 집합, 메인 하드락,isAccountNeedsReauth, 자격 증명 존재,deletedAt,codexValidationPending등만 보고, 플랜 티어는 입력으로 쓰지 않는다. 플랜 값 자체는 이미 로컬에 있다.src/codex/plan-from-token.ts가 JWTchatgpt_plan_type을codexAccounts[].plan에 맞추고,src/codex/plan.ts의isThirtyDayOnlyCodexPlan등이go/free를 특별 취급한다. 지금 유일한 제외 수단은src/codex/account-pause.ts의 수동pausedCodexAccountIds뿐이라, 운영자가 이미 장애를 본 뒤에야 손을 댄다.현재
devHEAD6101140ff(2.51.0) 기준으로 feasibility는 L3 #4212와 짝으로 #4211을 좁혔다. 핵심은 플랜 필터를isCodexAccountUsable에만 넣지 말고,src/codex/routing.ts의getEligiblePoolAccounts(약 1237행)에 두는 것이다. 그 함수가 이미 pause(isCodexAccountPaused), reauth, quota health, soft-avoid, 그다음isCodexAccountUsable을 한곳에서 걸러 내는 단일 입구이기 때문이다. 여기에 opt-inexcludedPlans또는minimumPlan을 넣으면, 명시적 라우트(특정 계정 id로 보낸 요청)는 그대로 두고 자동 풀 선택만 Free 등을 건너뛸 수 있다. 대시보드 계정 카드와 CLI 상태에는 플랜이 바뀌어 제외됐다는 이유를 보여 주는 쪽이 이슈 본문의 “What should OpenCodex do?”와 맞다.주의할 점 하나:
codexPool.excludedPlans/minimumPlan같은 키를 넣으면 config 표면에 손이 간다. types.ts/config.ts 쪼개기 캠페인과 겹칠 수 있으니, 키 이름과 스키마는 최소 한두 필드만 추가하고 넓은 config 리팩터는 하지 말자. 넓게 types/config를 흔들면 무효화될 수 있으므로, 그때는 close-don't-rebase / 대기 쪽이 맞다. 이번 범위는 “필터 한 줄 + 설정 키 최소 추가 + 상태 표시”면 충분하다.경로/심볼 -
src/codex/account-usability.tsisCodexAccountUsable: 플랜 미검사 (여기에만 넣지 말 것)라인 ~1237 -
src/codex/routing.tsgetEligiblePoolAccounts: pause/reauth 이후 플랜 정책 필터를 둘 올바른 위치경로/심볼 -
src/codex/plan-from-token.ts/src/codex/plan.ts:chatgpt_plan_type·free/go분류가 이미 있음경로/심볼 -
src/codex/account-pause.ts: 수동 pause만 있고 플랜 정책은 없음경로/심볼 - config 키
codexPool.excludedPlans또는minimumPlan: types/config 표면 최소 터치 필요메인테이너의 판단이 필요한 지점
excludedPlans: ["free"]로 할지minimumPlan: "plus"같은 하한으로 할지 (둘 다 opt-in)go등 다른 티어도 같은 정책에 넣을지너의 추천
opt-in
excludedPlans(또는minimumPlan)를getEligiblePoolAccounts에 넣고,isCodexAccountUsable단독 수정은 피하라. 명시적 계정 라우트는 유지하고, 대시보드/CLI에는 플랜 제외 이유를 보여 주라. config 키는 최소만 추가하고 types/config 대규모 쪼개기와 섞지 마라. L3 레인(#4212와 함께)에서 구현하라.이 댓글은 grok-bot이 작성했습니다
Merge pull request #4238 from lidge-jun/codex/260911-l3-pool-plan-policy
feat(codex): let an operator keep a downgraded account out of pool ro…
lidge-jun commentedon Sep 13, 2026
Resolved on
devby #4361, landed today as part of a 36-pull-request batch.isCodexAccountPlanExcludedinsrc/codex/routing.tsis applied bygetEligiblePoolAccounts, socodexPool.excludedPlanskeeps Free-tier accounts out of automatic rotation while an explicitly named account still routes. The CLI and dashboard surface the reason asplan_excluded/not-auto-selected(plan=...).#4238 referenced this issue but left the display work out of scope; #4361 completed it.
Verified against
origin/devat72601a173after the batch landed, rather than from the pull request description. Closing on that evidence; reopen if the behavior does not match on your setup.feat(codex): let an operator keep a downgraded account out of pool ro…
Merge pull request lidge-jun#4238 from lidge-jun/codex/260911-l3-pool…