Repository navigation
Native-profile lock deadlock: all native requests 503 with zero profiles registered, and account main recover cannot clear it #1120
Description
Client or integration
Codex CLI
Area
Authentication and account pool
Summary
The native-main-profile lock can enter a state where it is never released, and every native-routed request then fails with 503 server_is_overloaded / "Native Codex main profile is switching; retry this request".
The lock is held even when no native profiles are registered at all (profiles: [], activeProfileId: null, and an empty <CODEX_HOME>/.opencodex-native-main-profiles/ directory). The subsystem gates all traffic for a feature that has nothing configured in it.
The documented recovery command, ocx account main recover, cannot clear it: that command is a client of the proxy's management API, and the management API refuses while the lock is held. Stopping the proxy releases the lock at the filesystem level, but then every management command fails with Proxy is not running. There is no out-of-band break-glass path, so the state is unrecoverable through supported commands.
I expected either that the lock would not be taken when no profiles exist, or that ocx account main recover would be able to clear it.
Secondary usability problem: returning code: server_is_overloaded makes the Codex CLI render "Selected model is at capacity. Please try a different model." That points users at OpenAI quota exhaustion. It took a fair amount of digging to establish the request never left the machine — durationMs of 27-45ms and usageStatus: "unreported" were the giveaways.
Reproduction
Not consistently reproducible on demand; it appeared spontaneously and then recurred in bursts. What I observed:
ocx startwithcodexAccountMode: pool, a single Codex account (main, the Codex App login), and no native main profiles registered.- Send any request through the proxy to a native model, e.g.
POST http://127.0.0.1:10100/v1/responseswith{"model":"gpt-5.4-mini", ...}. - Observe
503returned in single-digit milliseconds withNative Codex main profile is switching. - Attempt the documented recovery — it fails on the same lock:
$ ocx account main doctor --json
Error: The native-profile lock is unavailable.
$ ocx account main recover --json
Error: The native-profile lock is unavailable.
$ ocx account main list --json
{"effectiveCodexHome":"<CODEX_HOME>","activeProfileId":null,"profiles":[]}
ocx restartclears the currently active burst but does not prevent recurrence.
Observed burst pattern over ~3 hours, across a proxy restart. Note the bimodal durations (0-5s vs 211-268s), which look like lock contention rather than a crash:
start end dur gap-since-prev-start
06:42:15 06:46:43 268s -
06:49:04 06:49:07 3s 7m
06:59:20 07:00:37 77s 10m
07:07:48 07:07:51 3s 8m
07:12:55 07:14:22 88s 5m
07:16:34 07:16:52 18s 4m
07:32:43 07:32:58 15s 16m
07:43:55 07:43:58 3s 11m
08:22:51 08:22:54 3s 39m
08:33:04 08:33:09 5s 10m
08:36:08 08:36:38 30s 3m
08:41:16 08:41:16 0s 5m
--- ocx restart here (08:43) ---
09:38:06 09:41:36 211s 57m
09:44:31 09:44:31 0s 6m
One correlation worth checking: a codex-quota-cache.json write landed at 09:38:19, 13 seconds after that burst began, which suggests the quota-refresh path takes the same lock.
Workaround that resolved it for me: ocx provider account-mode direct. With a single Codex account, pool mode adds nothing, and direct mode keeps the profile-switching layer out of the request path. Verified working end-to-end afterwards; no recurrence since.
Note the catch-22 in applying it: setting account-mode requires the proxy to be running, while clearing the lock requires it to be stopped. The window to apply the workaround is whatever clean interval happens to occur.
Suggested fixes
- Do not acquire the native-profile lock when no profiles are registered. If
profilesis empty andactiveProfileIdis null, the subsystem should be inert. - Give
recoveran out-of-band path that does not depend on the proxy's management API, e.g. an offline--forceoperating on the lock file directly while the proxy is stopped. A recovery tool gated behind the thing it recovers cannot function. - Add a lock lease/timeout so a holder that never completes releases automatically.
- Use a distinct error code instead of
server_is_overloaded, or name opencodex in the message, so users are not sent to investigate OpenAI quota. - Surface this in
ocx doctor, which reportedOAuth health: okwhile all traffic was failing.
Candidate source locations from code search: src/codex/native-profile-manager.ts, src/codex/native-profile-store.ts, src/codex/native-main-owner.ts, src/codex/native-main-claim.ts, src/codex/native-profile-startup.ts, src/server/management-api.ts, src/cli/doctor.ts.
Possibly a regression from the native-main-login work in #656.
Version
2.10.1 (also inspected dev at v2.10.2; no native-profile changes in that release, so this should still reproduce)
Operating system
Windows 11 Pro 10.0.26200
Provider and model
openai (native Codex) / gpt-5.4-mini, gpt-5.6-luna, gpt-5.6-terra — all native-family models failed identically
Logs or error output
# usage.jsonl record for a failing request
{
"provider": "openai",
"model": "gpt-5.4-mini",
"admissionKind": "loopback",
"inboundProtocol": "responses",
"status": 503,
"durationMs": 32,
"usageStatus": "unreported",
"errorCode": "server_is_overloaded",
"upstreamError": "Native Codex main profile is switching; retry this request",
"routeDecision": { "routeKind": "native" }
}
# reproduced directly against the proxy
POST http://127.0.0.1:10100/v1/responses
-> HTTP 503 in 0.0068s
{"error":{"message":"Native Codex main profile is switching; retry this request",
"type":"server_error","code":"server_is_overloaded"}}
# recovery blocked by the same lock
$ ocx account main doctor --json
Error: The native-profile lock is unavailable.
$ ocx account main recover --json
Error: The native-profile lock is unavailable.
# nothing registered in the subsystem holding the lock
$ ocx account main list --json
{"effectiveCodexHome":"<CODEX_HOME>","activeProfileId":null,"profiles":[]}
# lock file present and actively touched
<CODEX_HOME>/.opencodex-native-profile.lock.sqlite 8192 bytes
<CODEX_HOME>/.opencodex-native-main-profiles/ (empty)Screenshots and supporting files
No response
Redacted configuration
{
"providers": {
"openai": {
"adapter": "openai-responses",
"baseUrl": "https://chatgpt.com/backend-api/codex",
"codexAccountMode": "pool",
"authMode": "forward"
}
},
"defaultProvider": "openrouter",
"openaiProviderTierVersion": 2,
"activeCodexAccountId": "__main__"
}Account pool state at the time: a single Codex account (main, Codex App login). auto-switch: on (threshold 80%) with actual weekly usage around 47%, so the threshold was never approached.
Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
github-actions commentedon Aug 6, 2026
Automated translation bookkeeping — detected language: English.
Ingwannu commentedon Aug 6, 2026
Confirmed as a real local admission/locking bug on current dev (bbd82e7).
The lock file existing on disk is expected and is not itself proof that a transaction is held. The actionable code path is the startup/stage-sweep path: startNativeMainStartupLifecycle runs manager.sweepStages() under the native-profile SQLite transaction even when there is no stage registry and no staging tree. A transient lock or Windows ACL failure then makes runOwnedStageSweep fail, leaves the native-main gate in stage-cleanup-required/recovery state, and native requests surface the misleading 503. doctor and recover subsequently contend on the same profile transaction, which creates the reported catch-22.
I am preparing a focused fix that will:
I will link the dev-targeted PR here after the focused tests, typecheck, and privacy scan pass.
Ingwannu commentedon Aug 6, 2026
I opened draft PR #1124 against
devwith the absence-only fast path described above.The change skips the native-profile stage transaction only when both the stage registry and this instance's staging tree are proven absent. Any present artifact or observation error still enters the existing locked, fail-closed cleanup path. It also makes the 503 identify local OpenCodex native-profile maintenance instead of looking like upstream capacity pressure.
Focused tests pass 51/51, along with typecheck and the privacy scan. The full suite passed 9,417 tests and hit one load-sensitive failure in the Live/Realtime sideband test; that exact case then passed seven consecutive isolated/focused executions. I documented that result in the PR rather than presenting the run as fully green.
Because this touches the native-profile/authentication boundary, I requested review from @lidge-jun and @Wibias. I am leaving this issue open and the PR in draft until review and CI complete.
BeniSisaki commentedon Aug 6, 2026
I hit this exact symptom on 2.10.2 (Windows 11, pool mode, zero profiles) and instrumented the proxy to find the concrete mechanism — it may be the missing root cause here: the native-main owner acquisition fails permanently closed because the two startup
icaclscalls (ACL hardening) exhaust the shared 5sOPENCODEX_ACL_TIMEOUT_MSbudget, and the non-busy error branch indrive()publishes terminalunavailablewith no retry.Captured timings on my host: first icacls exit 0 after ~2.0s, second needed ~11.2s wall time in the daemon's startup window (same harden completes in ~230ms in a standalone bun process), so the shared deadline is always exhausted → gate blocked
owner-unavailable→ all native requests 503server_is_overloaded→ Codex renders "Selected model is at capacity". 37/37 requests failed while/healthzandocx statuswere green.Workaround that fully recovered the proxy for me:
setx OPENCODEX_ACL_TIMEOUT_MS 60000+ocx restart— doctor went healthy and a realgpt-5.6-lunarequest through the proxy returned 200. Full evidence, reproduction, and fix suggestions in #1156.Merge pull request #1134 from lidge-jun/codex/260806-stack02-native-p…
Wibias commentedon Aug 7, 2026
Closing as fixed on dev.
#1134 fixes the zero-profile startup case by skipping the native-profile stage sweep only when both the stage registry and staging tree are provably absent.
Existing fail-closed behavior is preserved when stage state exists or cannot be verified, and the misleading upstream-capacity-style 503 was also corrected.
Related Windows ACL issues such as #1156 remain separate and open.
fernandoAAGM22 commentedon Aug 8, 2026
Independent confirmation from the published
@bitkyc08/opencodex2.10.2 release on Windows 11, using Codex CLI 0.147.0.Observed behavior
Selected model is at capacity. Please try a different model.ocx statusreported the proxy healthy.Local routing evidence
127.0.0.1:10100was owned by the bundled OpenCodexbun.exeprocess.opencodex-proxyscheduled task/service was running.codex,codex.cmd, andcodex.ps1.openai_base_url = "http://127.0.0.1:10100/v1"codex doctor --allshowed the endpoint asws://127.0.0.1:10100/v1/...and the local proxy answered the WebSocket probe with HTTP 426 (Responses WebSocket transport is disabled; use HTTP).Control test
After running:
codex doctor --allshowed the native endpoint atwss://chatgpt.com/backend-api/....Result:
NATIVE_LUNA_OK.I did not instrument the proxy's ACL timing or capture the exact internal 503 body, so I cannot independently claim the precise
icaclsmechanism from #1156. This report only confirms the same misleading user-visible capacity symptom on 2.10.2, and that the native OpenAI route worked immediately once the OpenCodex proxy was removed.I found #1120 and #1156 before opening a new issue, so I am adding this as an independent released-version reproduction rather than creating a duplicate. Thank you for the fixes already landed on
dev.Merge pull request lidge-jun#1134 from lidge-jun/codex/260806-stack02…