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 Desktop hides all opencodex-routed models once Luna Reserve is active; gpt-reserve forwarded natively and rejected #4940
With opencodex injected as the Codex Desktop provider, all opencodex-routed models disappear from the Desktop model picker once the ChatGPT allowance is exhausted and Luna Reserve becomes active. The only request Desktop still sends, gpt-reserve, is forwarded natively by the proxy and rejected upstream, so no model is usable until the ChatGPT reset.
Expected: opencodex-routed models remain selectable and usable in Desktop while the ChatGPT allowance is exhausted, or ocx status / ocx doctor states that the injected models are hidden while Reserve is active. If Desktop can only emit gpt-reserve in this state, the proxy offers a way to keep an independently credentialed route usable.
Reproduction
Reproducible: always. Also reproduced after updating from 2.55.0-preview.20260914 to 2.58.0 and restarting Desktop.
ocx ensure; confirm anthropic and custom models appear in the Desktop picker while ChatGPT quota is available.
Use Codex until the ChatGPT weekly allowance reaches 100%. The Desktop widget switches to "Luna Reserve · 100% remaining".
Open the Desktop model picker. Only GPT-5.6 Luna is listed; all opencodex-routed models are gone.
Send a message with GPT-5.6 Luna. Desktop shows "You've hit your usage limit. Add credits to continue, or try again at Sep 19, 2026, 2:20 PM."
Version
2.58.0 (npm latest), bundled Bun 1.4.0, launchd service on loopback port 10100. Codex Desktop 26.911.61220 (build 9647), bundled codex-cli 0.155.0-alpha.2.6.
Operating system
macOS 26.6 (25G5057c), arm64
Provider and model
openai (ChatGPT Pro account pool) / gpt-reserve, native route. Default provider anthropic (OAuth) / claude-fable-5-1; one custom openai-responses provider. routing=opencodex-local, multi_agent_mode: v1, codexDesktopAuthless not enabled.
Logs or error output
ocx logs entry for the failing send, 2026-09-17 21:34:26 CEST:
429 openai-<acct>/gpt-reserve 3915ms routeKind=native
errorCode=rate_limit_exceeded upstreamError="The usage limit has been reached"
The 264 Desktop requests for gpt-5.6-luna the same day were shadow-rewritten to claude-sonnet-5 (shadowCallRewrittenFrom), so Reserve traffic only exists as gpt-reserve. ocx doctor reports the proxy healthy and the 2.58.0 CLI matching the running proxy. Quota cache for the main account: weeklyPercent 100, weeklyResetAt 2026-09-19 14:20 CEST, resetCredits 0.
Screenshots and supporting files
Screenshots of the usage widget and the collapsed picker will be attached in a comment.
Redacted configuration
Stock ocx ensure injection: openai_base_url pointed at the loopback proxy, model_catalog_json from ~/.codex/opencodex-catalog.json. No codexDesktopAuthless, no codexAccountNamespaces.
Checks
I searched existing issues and documentation.
I removed secrets, tokens, account details, request credentials, and personal data.
We investigated this exact failure mode today in ChatGPT Desktop (macOS ARM64, Electron 26.911.61220) and identified both the root cause and a robust, working workaround that opencodex could easily integrate natively.
1. Root Cause Breakdown
There are actually two distinct layers of locking in the Desktop app when ChatGPT quota is depleted:
UI Model Picker Lockdown & gpt-reserve emission:
When the primary allowance reaches 100%, /backend-api/wham/usage returns rate_limit: { allowed: false } and rate_limit_upsell: { banner_type: 'luna_reserve' }.
The Desktop React state sets isLunaReserveActive = true, which forcefully overrides the active model to gpt-reserve (labeled as 🌙 GPT-5.6 Luna) and collapses/hides the custom model dropdown.
When the user sends a message, Desktop emits model: "gpt-reserve". Since opencodex forwards gpt-reserve natively to OpenAI (https://chatgpt.com/backend-api/codex/responses), OpenAI rejects it with 429 usage_limit_reached once reserve capacity is zero.
Hard Composer Lockout (Send Button Disabled):
Once Luna Reserve / account credits are completely exhausted (0 credits, rate_limit_reached_type !== null), the Recoil atom in Desktop (Cro/wro) transitions to hardBlocked: true.
This causes the composer components (f9 -> Exr -> plr) to enforce submitDisabled: true, completely disabling the send button (aria-label="Enviar" / Send) and rendering the blocking modal ("You've run out of usage for Codex...").
2. The Architectural Discovery in Electron (main-*.js)
In the Desktop Electron main process:
The base URL resolver checks process.env.CODEX_API_BASE_URL:
Crucially, localhost:8000 is explicitly whitelisted in isDesktopAuthAllowedUrl(e) alongside native endpoints, meaning Desktop happily attaches OAuth Bearer tokens, DeviceCheck headers, and account IDs when talking to http://localhost:8000/backend-api!
3. The Working Solution
We implemented a two-part solution that completely restores full functionality:
Part A: Transparent Unblocker Proxy on port 8000
A lightweight local proxy on 127.0.0.1:8000 transparently passes through all endpoints to https://chatgpt.com, exceptGET /backend-api/wham/usage:
When intercepting /wham/usage, it decompresses gzip/br/deflate, and overrides:
With CODEX_API_BASE_URL="http://localhost:8000/backend-api" set in LaunchServices (launchctl setenv), Desktop never enters hardBlocked: true. The upsell modal disappears, and the composer send button remains fully enabled.
Part B: Model Redirection in OpenCodex
To handle cases where Desktop forces gpt-reserve or gpt-5.6-luna:
In ~/.opencodex/config.json, configure model redirects:
When Desktop sends a prompt targeting gpt-reserve, the opencodex router rewrites it to your configured provider (e.g. deepseek/deepseek-flash or anthropic/claude-sonnet-5), executing the prompt with full reasoning and returning HTTP 200 to Desktop!
4. Recommendation for OpenCodex Upstream
@lidge-jun Opencodex could incorporate this directly so users don't need manual workarounds:
Built-in gpt-reserve Routing:
In src/router.ts (or src/codex/luna-reserve-hop.ts), if upstream returns isLunaQuotaRejection(429) or if defaultProvider !== 'openai', optionally redirect gpt-reserve to the configured default provider instead of failing with 429.
Native ocx desktop unblock / Port 8000 Listener:
Opencodex could bind a secondary listener on 127.0.0.1:8000 (or multiplex via CODEX_API_BASE_URL) to mock /wham/usage with allowed: true. A CLI command like ocx desktop-bypass on/off could automatically manage launchctl setenv CODEX_API_BASE_URL and LaunchAgent lifecycles.
Happy to provide a PR or detailed code samples for the unblocker service if the maintainers would like to integrate it into ocx!
Fixed on dev by #4968 at 277291f — local refusal when codexDesktopAuthless is unset so gpt-reserve is no longer forwarded as a native model into an unhelpful upstream 429.
Activity
github-actions commentedon Sep 17, 2026
Issue reopened
The report now contains the information required by the automated check. Thanks for updating it.
Rizek000 commentedon Sep 17, 2026
guilhermemarketing commentedon Sep 17, 2026
We investigated this exact failure mode today in ChatGPT Desktop (macOS ARM64, Electron
26.911.61220) and identified both the root cause and a robust, working workaround that opencodex could easily integrate natively.1. Root Cause Breakdown
There are actually two distinct layers of locking in the Desktop app when ChatGPT quota is depleted:
UI Model Picker Lockdown &
gpt-reserveemission:/backend-api/wham/usagereturnsrate_limit: { allowed: false }andrate_limit_upsell: { banner_type: 'luna_reserve' }.isLunaReserveActive = true, which forcefully overrides the active model togpt-reserve(labeled as 🌙 GPT-5.6 Luna) and collapses/hides the custom model dropdown.model: "gpt-reserve". Since opencodex forwardsgpt-reservenatively to OpenAI (https://chatgpt.com/backend-api/codex/responses), OpenAI rejects it with429 usage_limit_reachedonce reserve capacity is zero.Hard Composer Lockout (Send Button Disabled):
rate_limit_reached_type !== null), the Recoil atom in Desktop (Cro/wro) transitions tohardBlocked: true.f9->Exr->plr) to enforcesubmitDisabled: true, completely disabling the send button (aria-label="Enviar"/Send) and rendering the blocking modal ("You've run out of usage for Codex...").2. The Architectural Discovery in Electron (
main-*.js)In the Desktop Electron main process:
process.env.CODEX_API_BASE_URL:localhost:8000is explicitly whitelisted inisDesktopAuthAllowedUrl(e)alongside native endpoints, meaning Desktop happily attaches OAuth Bearer tokens, DeviceCheck headers, and account IDs when talking tohttp://localhost:8000/backend-api!3. The Working Solution
We implemented a two-part solution that completely restores full functionality:
Part A: Transparent Unblocker Proxy on port 8000
A lightweight local proxy on
127.0.0.1:8000transparently passes through all endpoints tohttps://chatgpt.com, exceptGET /backend-api/wham/usage:/wham/usage, it decompresses gzip/br/deflate, and overrides:{ "rate_limit": { "allowed": true, "limit_reached": false, "primary_window": { "used_percent": 0 } }, "rate_limit_reached_type": null, "rate_limit_upsell": null, "credits": { "has_credits": true, "unlimited": true, "balance": "1000" } }CODEX_API_BASE_URL="http://localhost:8000/backend-api"set in LaunchServices (launchctl setenv), Desktop never entershardBlocked: true. The upsell modal disappears, and the composer send button remains fully enabled.Part B: Model Redirection in OpenCodex
To handle cases where Desktop forces
gpt-reserveorgpt-5.6-luna:~/.opencodex/config.json, configure model redirects:gpt-reserve, the opencodex router rewrites it to your configured provider (e.g.deepseek/deepseek-flashoranthropic/claude-sonnet-5), executing the prompt with full reasoning and returning HTTP 200 to Desktop!4. Recommendation for OpenCodex Upstream
@lidge-jun Opencodex could incorporate this directly so users don't need manual workarounds:
gpt-reserveRouting:In
src/router.ts(orsrc/codex/luna-reserve-hop.ts), if upstream returnsisLunaQuotaRejection(429)or ifdefaultProvider !== 'openai', optionally redirectgpt-reserveto the configured default provider instead of failing with 429.ocx desktop unblock/ Port 8000 Listener:Opencodex could bind a secondary listener on
127.0.0.1:8000(or multiplex viaCODEX_API_BASE_URL) to mock/wham/usagewithallowed: true. A CLI command likeocx desktop-bypass on/offcould automatically managelaunchctl setenv CODEX_API_BASE_URLand LaunchAgent lifecycles.Happy to provide a PR or detailed code samples for the unblocker service if the maintainers would like to integrate it into
ocx!Rizek000 commentedon Sep 17, 2026
huge! thx @guilhermemarketing
Rizek000 commentedon Sep 17, 2026
@lidge-jun looks like you are already on top of this but can you include this issue in you threads?
feat(codex): add desktop unblocker loopback listener on port 8000 (li…
docs(devlog): record the lane I catalog and model-visibility unit (#4973
fix(codex): refuse Reserve turns the Desktop authless opt-in cannot s…
lidge-jun commentedon Sep 18, 2026
Fixed on
devby #4968 at 277291f — local refusal whencodexDesktopAuthlessis unset sogpt-reserveis no longer forwarded as a native model into an unhelpful upstream 429.