Repository navigation
Description
Problem
bin/codex-proxy.mjs's exec transports have no turn deadline. runCodexOnce (bin/codex-proxy.mjs:803, spawn at :815) returns a promise that settles only from the child's close handler (:921) or a synchronous spawn throw — there is no timer anywhere in the function. Both exec call sites await it directly: the warm-resume attempt (:1219) and the cold full-history run (:1242). If the spawned codex exec child hangs — alive but never finishing its turn (upstream stall, a CLI waiting on something, a wedged session resume) — the HTTP request hangs forever, the child process is never killed, and the promise never settles. The only recovery is the client giving up; even then nothing in runCodexOnce kills the child on client disconnect (no res.on('close') hook reaches it — onSpawn only registers the child for the caller's bookkeeping).
Every sibling path in the same file, and in the sibling proxies, is bounded:
- The app-server transport in this same file: connect timeout 10s (
:504), JSON-RPC timeout 30s (:635-641). bin/claude-code-proxy.mjs:TURN_TIMEOUT_MS = 300 * 1000(:55) applied to its spawned turns (:455-461), with a SIGKILL escalation 3s after the deadline (:480).bin/muse-proxy.mjs's warm path:WARM_TURN_TIMEOUT_MS = 300 * 1000(:300, applied:585-587).
The exec fallback is precisely the path taken when the bounded app-server path has already failed — i.e. when the backend is already misbehaving — and it is the one path with no bound at all.
Evidence (verified this run, main b2b885b9)
Sandbox probe with a fake codex binary ($HOME/.local/bin/codex under a scratch HOME; the fake answers debug models with garbage so the static catalog floor is used, and sleeps 60s for any other invocation), CODEX_PROXY_TRANSPORT=exec:
- Proxy log:
Spawning: .../codex exec --json --skip-git-repo-check ... -m gpt-5.6-luna ...— the request reachedrunCodexOnce's spawn. curl -X POST /v1/chat/completions(non-streaming): no response after 8s (curl killed bytimeout 8, exit 124, 0 response bytes) while the fake child (sleep 60) was still running. Nothing in the proxy would have ended the request before the child's own exit at 60s — and a truly hung child never exits.
Dedup: gh searches for codex-proxy exec timeout, runCodexOnce, and codex-proxy hang (open + closed) return no covering issue. Adjacent but distinct: #2097/#2100/#2102 are the same no-timeout class in design-code-proxy, clef-proxy, and antigravity-ls-proxy (HTTP fetch / RPC paths in other files); the muse-proxy exec path has the same defect and is filed separately this run.
Why it matters
code-queue workers and OpenCode sessions route real coding turns through this proxy. One hung codex exec child wedges its request indefinitely and leaks a process; repeated hangs accumulate children and stall every caller queued behind them, with no log line or error to act on. The fallback path exists for resilience — unbounded, it converts a backend stall into a permanent proxy-side hang.
Acceptance criteria
- A
codex execturn that exceeds a named deadline (consistent with the house convention, e.g. claude-code-proxy's / muse-proxy's 300s turn timeout, or aCODEX_PROXY_*env override parsed strictly) is terminated (SIGTERM, then SIGKILL after a short grace) and the request settles with an error result through the existingdone({ok:false, ...})shape. - The deadline applies to both
runCodexOncecall sites (warm resume and cold run); the app-server transport's existing timeouts are unchanged. - A client disconnect before the deadline also kills the exec child (no orphaned
codex execprocesses). - Regression tests in
bin/codex-proxy.test.mjsstyle: a fake child that never exits is killed at the deadline and the request errors; a fake child that completes normally is unaffected. - No change to the success/failure semantics judged from
turn.failed/ exit code (codex-proxy: a failed codex run is reported as a successful 'stop' — close handler ignores exit code and signal in both modes #676/codex-proxy: a turn.failed event with exit 0 is reported as a successful 'stop' — #677 judged failure by exit code only, no terminal-event check #680 behavior preserved for children that do exit).
Activity
developing-today commentedon Oct 29, 2026
CODE-1052
dezren39 commentedon Oct 29, 2026
🤖 Agent note · filed
codexbinary that the exec transports (runCodexOnce) have no turn deadline — request unsettled at 8s, child never killed — while the app-server path in the same file and both sibling proxies bound their turns.dezren39 commentedon Oct 29, 2026
Cross-link: the muse-proxy exec fallback path has the same no-deadline defect and is filed as #2159 (verified the same way this run). The two fixes can share the deadline convention pinned in triage.
63 remaining items
dezren39 commentedon Nov 4, 2026
🤖 Agent note · handed off
dezren39 commentedon Nov 4, 2026
🤖 Agent note · handed off
dezren39 commentedon Nov 4, 2026
agent-note:v1
task: 8808
state: handed off
event: handed_off
lane: opencode
execution_lane: opencode
session_id: ses_e5e58cf03bbffeNSSiAFL9ZcVC
note: OpenCode claimant launched and bound this worker session before sending the task packet.
developing-today commentedon Nov 4, 2026
🤖 Agent note · handed off
opencode-claimant)repo-health-code-pilot, workerrepo-worker-code)ses_e5e58648c8bffeAkJejxNl5nwBopencode-route/workhorse; agentbuilddezren39 commentedon Nov 4, 2026
🤖 Agent note · done
a335f212a844035b74a2d388ad99ff3c16bbf590is currentmain. Issue codex-proxy: exec transports have no turn deadline — a hung codex exec child hangs the request forever and is never killed #2157 closed automatically.CODEX_PROXY_TRANSPORT=exec-resume node --test bin/codex-proxy.test.mjs— 33 passed; bothnode --checkcommands passed;.github/scripts/check-proxy-port-fallbacks.shpassed on the merged tree.ses_e5e58648c8bffeAkJejxNl5nwB