Skip to content

Codex Luna Reserve / gpt-reserve mode disables OpenCodex routed models after 5-hour quota is exhausted #2813

Closed

Description

@Wujerry

Client or integration

Codex App / Codex model picker with OpenCodex routed models.

Area

Catalog / models / Codex client compatibility.

Version

2.34.0

Operating system

Windows 11 25H2

Summary

After a recent Codex update, when the normal 5-hour Codex quota is exhausted, Codex offers a reserve fallback model (gpt-reserve / Luna Reserve) so the user can continue working.

Once this reserve mode becomes active, other models in the Codex model picker become unavailable and cannot be selected. This also affects models injected/routed by OpenCodex, even though those third-party/provider models do not necessarily consume the exhausted ChatGPT/Codex quota.

This makes OpenCodex routed models effectively unusable from the picker for the rest of the 5-hour quota window.

Current behaviour

Before the 5-hour Codex quota is exhausted:

  • OpenCodex routed models are present in the model picker.
  • They can be selected normally.
  • Requests route through OpenCodex as expected.

After the 5-hour Codex quota is exhausted:

  • Codex activates/offers gpt-reserve (Luna Reserve) as a fallback.
  • Other models become unavailable in the model picker.
  • OpenCodex routed models are also no longer selectable.

The important part is that the reserve/quota gating appears to apply globally to the picker rather than only to native ChatGPT/Codex models.

Expected behaviour

OpenCodex routed models that use independent providers/credentials should remain selectable when the native Codex 5-hour quota is exhausted.

If the Codex client itself performs this availability check before requests reach OpenCodex, it would be useful for OpenCodex to investigate whether there is a compatible catalog representation, alternate selection path, or documented workaround that keeps routed models usable during Luna Reserve mode.

At minimum, OpenCodex should document this as a Codex client compatibility limitation if it cannot be worked around from the proxy/catalog side.

Reproduction

  1. Configure OpenCodex and sync routed models into Codex.
  2. Verify that routed models can be selected normally from the Codex model picker.
  3. Use Codex until the normal 5-hour usage limit is exhausted.
  4. Observe that Codex offers/activates gpt-reserve / Luna Reserve.
  5. Open the model picker again.
  6. Try to select an OpenCodex routed model.
  7. The routed model is unavailable / cannot be selected, together with the other non-reserve models.

Why this looks like a compatibility issue

The failure happens at model selection time, so it may be caused by Codex applying subscription-quota availability rules to every catalog entry, including custom/routed OpenCodex entries, before OpenCodex gets a chance to route the request.

That would mean a native ChatGPT quota state is unintentionally disabling models backed by unrelated providers.

Suggested investigation

  • Check how the latest Codex client represents the Luna Reserve / gpt-reserve state in its model catalog and picker.
  • Determine whether custom OpenCodex catalog entries can opt out of native ChatGPT quota gating.
  • Check whether explicitly selecting a routed model by model ID still works even when the picker disables it.
  • If direct model selection works, consider documenting/exposing that as a temporary workaround.
  • If Codex rewrites or rejects the selected model before the request reaches OpenCodex, document the upstream limitation and, if possible, coordinate an upstream Codex fix.

Additional context

This started after the recent Codex update that introduced/exposed the reserve fallback after the normal 5-hour limit is reached.

I searched the existing OpenCodex issues and did not find a report specifically covering gpt-reserve / Luna Reserve disabling OpenCodex routed models.

Checks

  • I searched existing issues.
  • I removed credentials and personal data.
  • This report distinguishes native Codex quota from independently routed OpenCodex provider models.

Activity

  1. added
    bugSomething isn't working
    account-poolOAuth, credentials, Codex pool, quota, failover, plans
    on Aug 28, 2026
  2. github-actions commented on Aug 28, 2026

    @github-actions
    Contributor

    Issue reopened

    The report now contains the information required by the automated check. Thanks for updating it.

  3. lidge-jun commented on Aug 28, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 69 / 80

    설명

    이 이슈는 Codex 앱이 일반 5시간 할당량을 다 쓰면 Luna Reserve(gpt-reserve)로 넘어가면서, OpenCodex가 넣어 준 라우트 모델까지 피커에서 고를 수 없게 된다는 보고다. 보고자는 Windows 11과 OpenCodex 2.34.0이다. 지금 dev HEAD는 80e251a4d이고 패키지는 2.35.0이다. 방금 들어온 #2815는 Windows에서 coordinator adoption 파일을 읽기 전용으로 열어 fsync하다가 EPERM이 나던 수정이다. 피커나 할당량과는 관계가 없다.

    지금 카탈로그 동기화는 ChatGPT 5시간 할당량을 보고 줄을 숨기지 않는다. src/codex/catalog/sync.ts의 deriveEntry는 라우트 모델에 visibility를 list로 찍는다. 템플릿이 없으면 supported_in_api도 true로 둔다. src/codex/catalog/metadata.ts의 applyNativeVisibility는 네이티브 줄만 disabledModels나 계정 숨김으로 hide 한다. 슬래시가 있는 라우트 슬러그는 여기서 건드리지 않는다. src/codex/catalog/provider-fetch.ts의 filterCatalogVisibleModels도 disabledModels와 selectedModels만 본다. 5시간 창 값인 shortPercent는 이 세 곳에 없다.

    5시간 창 자체는 src/codex/quota.ts에 이미 있다. isCodexQuotaExhausted는 shortPercent가 100이면 그 계정을 소진으로 본다. 이건 라우팅과 풀 전환(#1791)용이다. src/codex/subagent-model-fallback.ts의 isNativeModelQuotaExhausted도 스폰 자식 모델을 바꿀 때만 쓴다. 둘 다 Codex 앱 피커를 잠그지 않는다. OpenCodex 카탈로그에 gpt-reserve나 Luna Reserve 슬러그도 없다. 네이티브는 gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna다.

    그래서 지금 코드만 보면, 피커가 Reserve만 남기는 동작은 OpenCodex가 카탈로그를 다시 써서 생기는 일이 아닐 가능성이 크다. Codex 앱이 5시간 소진을 보고 피커 전체를 Reserve 전용으로 바꾸는 쪽에 가깝다. 프록시가 요청을 받기 전에 클라이언트에서 막히면, visibility를 list로 유지해도 소용이 없다. 아직 확인이 필요하다. Reserve가 켜진 뒤 models.json / opencodex-catalog.json이 바뀌었는지, config.toml의 model에 라우트 슬러그를 직접 넣으면 요청이 OpenCodex까지 오는지부터 봐야 한다.

    #1711은 크레딧 0인 줄을 회색으로 만들자는 기능 요청이라 축이 다르다. #2706은 Luna에 effort를 강제로 낮추는 다른 버그다. types.ts/config.ts 스플릿에 새 설정 키를 지금 넣으면 그 캠페인에 쓸려 가니, 확인 전에는 키를 추가하지 마라. preview 배포도 이 이슈와 상관없다.

    라인 311 (deriveEntry visibility = list) - 라우트 줄은 5시간 소진과 상관없이 피커에 남도록 찍힌다. 여기서 숨기는 코드는 없다.
    라인 378 (템플릿 없는 폴백 supported_in_api: true) - API 노출도 할당량과 무관하다. 피커 잠금의 원인이 되기 어렵다.
    경로 applyNativeVisibility (src/codex/catalog/metadata.ts 441-461) - 네이티브만 disabledModels로 hide 한다. 슬래시 라우트 슬러그는 continue로 빠져나간다.
    경로 filterCatalogVisibleModels (src/codex/catalog/provider-fetch.ts 1634) - disabledModels와 selectedModels만 본다. shortPercent를 읽지 않는다.
    경로 isCodexQuotaExhausted (src/codex/quota.ts 115-129) - 5시간 창은 라우팅/풀 전환용이다. 카탈로그 visibility를 바꾸지 않는다.
    경로 gpt-reserve / Luna Reserve - 현재 카탈로그와 네이티브 슬러그 목록에 없다. 클라이언트 전용 모드일 가능성이 크다.
    경로 재현 - Reserve 켜기 전후 카탈로그 덤프와, config.toml에 라우트 슬러그를 직접 넣었을 때 프록시 로그가 있는지 아직 없다.

    메인테이너의 판단이 필요한 지점

    • 이건 OpenCodex 카탈로그 버그로 볼지, Codex 앱이 피커 전체를 Reserve로 잠그는 업스트림 호환 문제로 볼지.
    • 작성자에게 Reserve 전후 카탈로그 덤프와 직접 슬러그 선택 재현을 먼저 받을지, 메인테이너가 Codex 앱에서 바로 재현할지.
    • 클라이언트가 커스텀 줄을 할당량 게이트에서 빼 주는 필드가 있다면 그걸 찾을지, 없으면 문서로만 남기고 업스트림에 넘길지.
    • 확인 전에 카탈로그 필드나 설정 키를 추측해서 넣는 PR을 받을지. 받으면 스플릿 캠페인과 겹친다.

    너의 추천
    이슈는 열고, 코드 PR은 지금 받지 마라. 먼저 Reserve가 켜진 뒤 카탈로그 파일이 실제로 바뀌는지와, config.toml의 model에 라우트 슬러그를 넣으면 요청이 OpenCodex에 도달하는지를 확인해라. 카탈로그는 그대로인데 피커만 잠기면 업스트림 한계로 적고 문서화해라. 카탈로그가 같이 줄면 그때 shortPercent가 visibility에 섞인 경로를 찾아라. 새 설정 키는 넣지 마라.

    이 댓글은 grok-bot이 작성했습니다

  4. lidge-jun commented on Aug 28, 2026

    @lidge-jun
    Owner

    Investigated this again from source on current dev, and I could not find an OpenCodex-side gate that would disable routed models when Reserve activates. Recording what was ruled out, since a bare "cannot reproduce" is not much use to you.

    Checked and excluded:

    • Routed catalog rows are always written with visibility: "list"; native visibility logic explicitly skips namespaced routed entries.
    • Catalog filtering reads only disabledModels and each provider's selectedModels. It never consults quota.
    • shortPercent exhaustion is confined to native Codex account selection and does not reach provider routing.
    • An explicit provider/model request routes straight to the configured provider without consulting Codex quota at all.
    • gpt-reserve and "Luna Reserve" appear nowhere in the OpenCodex source, nor in the public openai/codex source.

    Two related fixes landed during this work but neither can explain your symptom: a52d00a6e only skips failover candidates inside the native account pool, and #2858 (676a3c03a) retries automatic compaction through a routed target — that runs only after a request reaches OpenCodex, so it cannot re-enable a picker row the client has already greyed out.

    That points to a Codex App-side Reserve gate, which OpenCodex cannot see or influence. To pin it down we would need, from an account actually in Reserve mode: before/after dumps of opencodex-catalog.json and models_cache.json (model/provider/status fields are enough — please omit tokens), and the result of setting the routed model slug directly in config.toml rather than picking it in the UI, plus whether any request then appears in the OpenCodex request log. If a request never arrives, the gate is client-side and this issue belongs upstream.

    Leaving this open pending that evidence rather than closing it as unreproducible.

  5. lidge-jun commented on Aug 29, 2026

    @lidge-jun
    Owner

    PR #2862 on dev fixes a real defect found while investigating this — routed catalog rows were carrying native eligibility metadata they have no business carrying. Whether that is your Reserve-mode failure is not established, and this stays open.

    What changed

    Two problems, both in how a routed catalog row gets built:

    Template selection could pick an ineligible native row. Three sites chose a native template to base a routed row on, using a lookup that accepted any native row at all. They now use a roster-restricted lookup that only considers supported models.

    Notably the four validity checks that use the same permissive lookup were deliberately left alone. They answer a different question — "is this catalog usable at all" — and narrowing them would reject a valid catalog whose only native row is a new one, falling back to stale data. There is now a regression test guarding that distinction, because the two contracts share one function and it would be easy to unify them wrongly.

    Five eligibility fields leaked onto routed rows. supported_in_api is now forced true and available_in_plans, minimal_client_version, availability_nux, and upgrade are dropped for routed rows. The sanitizing happens in the strict-field pass rather than the normalizer, so rows that were preserved from a previous catalog or came from elsewhere are covered too — not just freshly normalized ones.

    Both are routed-only. Native rows keep their metadata untouched.

    What this does not establish

    That this is what disables your routed models under Reserve. The mechanism is plausible — a routed row carrying plan-gating metadata is exactly the kind of thing a client would use to hide a model once a quota window closes — but we could not observe the app's behavior under Reserve. The docs note added for this says so explicitly rather than implying it is solved.

    What Codex does with these fields. That is client-side and closed to us. We can only stop sending metadata that should not be there.

    What would confirm it

    If you can reproduce after the next release:

    1. Exhaust the 5-hour window into Reserve, then capture the catalog response OpenCodex serves — specifically whether any routed row still shows available_in_plans or a non-true supported_in_api.
    2. Whether routed models disappear from the picker, or appear and fail on use. Those point at different layers.
    3. Whether gpt-reserve differs from ordinary quota exhaustion for you, since only the former was reported.

    Documentation for the limitation is in docs-site/ under "Native quota fallback limitation", in English and all seven translated locales, each stating that app behavior under Reserve is unverified.

  6. Tariolle commented on Aug 30, 2026

    @Tariolle

    I have the exact same bug and my OpenCodex is up to date, when the Luna Reserve kicks in the Codex Desktop UI doesn't show me any model except Luna, which makes OpenCodex effectively useless until I get my 5 hours limit back...

    Image
  7. lidge-jun commented on Aug 31, 2026

    @lidge-jun
    Owner

    @Tariolle Thank you — this changes the status of the issue, so I want to be explicit about what your report establishes.

    What is now different. My earlier comment recorded that I could not find an OpenCodex-side gate that disables routed models when Reserve activates, and #2862 landed a real defect found while looking (routed catalog rows carrying native eligibility metadata they should not carry). The important detail: #2862 shipped in v2.36.0 — I confirmed with git describe --tags --contains 124a2b148 → v2.36.0^2~88. So if you are on an up-to-date install, you already have that fix and are still seeing the symptom.

    That means #2862 was not the cause of what you and @Wujerry are hitting. Two independent reporters, one of them on a build that already contains the candidate fix, is no longer a "cannot reproduce" situation. I am treating this as an open, reproducible defect rather than an unconfirmed one.

    What would move this fastest. Your screenshot shows the picker collapsed to Luna only, which is the outcome; I need the catalog state that produced it. On the affected install, while Reserve is active:

    # 1. what the proxy is actually serving
    curl -s localhost:<port>/v1/models -H 'Authorization: Bearer <admin token>' | jq '[.data[].id]'
    
    # 2. what the management side thinks the catalog holds
    curl -s localhost:<port>/api/models -H 'Authorization: Bearer <admin token>' | jq '[.[] | {id, visibility, kind: .opencodex_catalog_kind}]'
    
    # 3. the installed version, so I am not guessing
    ocx --version

    The specific question those answer: are the routed rows missing from what OpenCodex serves, or are they present in the response and the Codex client is filtering them out on its side? Those are different bugs with different fixes, and the picker screenshot cannot distinguish them. If the rows are present in /v1/models while the picker shows only Luna, the fault is downstream of the proxy and I need to look at how Reserve mode rewrites the client-side catalog — which is a path I have not audited yet.

    Redact account identifiers freely; I only need ids and visibility fields.

    Reopening the investigation on this. Sorry it took a second reporter to get here — the earlier "ruled out" list was accurate about what I checked, and incomplete about what was actually happening.

  8. lidge-jun commented on Aug 31, 2026

    @lidge-jun
    Owner

    Triaged in the 2026-08-31 bug round and kept open deliberately. This is one of the few we could not resolve from the tree, and it is worth saying precisely why rather than leaving it to age.

    What we checked. No quota value participates in routed visibility anywhere on dev:

    • deriveEntry writes visibility: "list" (src/codex/catalog/sync.ts:311)
    • applyNativeVisibility skips slash-routed slugs (src/codex/catalog/metadata.ts:441)
    • filterCatalogVisibleModels reads only disabledModels and selectedModels (src/codex/catalog/provider-fetch.ts:1692)
    • isCodexQuotaExhausted is pool/routing only (src/codex/quota.ts:115)

    There is no OpenCodex Reserve gate to fix. #2862 (124a2b148) already strips available_in_plans and forces supported_in_api: true on routed rows, and a second reporter still saw Luna-only on a build containing it. So one blind catalog-field change has already been tried and did not work; a second guess would be guessing harder, not better.

    Why we cannot close it either. A picker screenshot cannot distinguish "the proxy never advertised the row" from "the client filtered a row the proxy did advertise", and those two have opposite fixes. The docs already record the client gate as unverified (docs-site/src/content/docs/guides/codex-app-models.md:113).

    What would unblock this, from an account actually in Reserve at the time:

    1. ocx --version
    2. GET /v1/models — the raw id list
    3. GET /api/models — {id, visibility, opencodex_catalog_kind} per row
    4. With model = "provider/slug" set in config.toml, whether a request produces a proxy request-log entry at all

    Item 4 is the decisive one: a log entry means the proxy served it and the client hid it; no entry means the request never arrived. Everything else follows from that.

  9. added a commit that references this issue on Sep 1, 2026
    59449fa
  10. lidge-jun commented on Sep 1, 2026

    @lidge-jun
    Owner

    Documented in #3202 (merged to dev as 59449fa).

    Root cause, verified against the installed Codex desktop app renderer bundle (2026-08-31 build): the reserve gate is entirely client-side and never looks at the model catalog.

    • Reserve is considered active when the app's own backend-api/wham/usage poll returns rate_limit_upsell.banner_type === "luna_reserve", rate_limit.allowed === false, and additional_rate_limits[] contains { limit_name: "gpt-reserve", rate_limit.allowed: true }.
    • While that holds, the model-settings hook force-overrides modelSettings.model to gpt-reserve and rewrites any other pick back to it (setModelAndReasoningEffortForNextTurn(reserveActive && model === "gpt-reserve" ? original : model, ...) and the picker collapses to the reserve entry). This happens before any request leaves the app, so a config.toml model = value is overridden the same way — that route is not a workaround.
    • Upstream codex-rs only carries rate_limit_reached_type / x-codex-rate-limit-reached-type; the Luna Reserve gating lives in the Electron app.

    opencodex routed rows are still published with visibility: "list" and no quota value participates in catalog filtering; the proxy side is correct. The docs now state this plainly and point to the paths that do work while the window is exhausted: ocx claude, ocx access test <model> --protocol responses, or any direct /v1 client.

    Closing as documented; no proxy-side change can lift a gate the client applies to itself.

  11. github-actions commented on Sep 1, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: English.

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