Repository navigation
Description
Problem
bin/muse-proxy.mjs's exec (cold) path has no turn deadline. When the warm MSP path is disabled (MUSE_PROXY_WARM=0) or a request falls back to exec, the handler spawns muse exec (bin/muse-proxy.mjs:979) and the HTTP response is completed only from the child's close handler (:1083). There is no timer on this path: the only kill is on client disconnect (res.on('close') → child.kill('SIGTERM'), :992-996). If the spawned muse exec child hangs — alive but never emitting a terminal event (upstream stall, a CLI waiting on input the proxy never sends, a wedged run) — and the client is a patient programmatic caller (an OpenCode session, a queue worker), the request hangs forever and the child accumulates.
Every sibling path is bounded, including in this same file:
- The warm path in this file:
WARM_TURN_TIMEOUT_MS = 300 * 1000(:300), applied per turn (:585-587); MSPrpc()defaults to a 30s timeout (:513-519); even the--helpcapability probe kills its child after 10s (:216-219). bin/claude-code-proxy.mjs:TURN_TIMEOUT_MS = 300 * 1000(:55) on its spawned turns, with SIGKILL escalation (:480).bin/codex-proxy.mjs's app-server path: 10s connect / 30s RPC timeouts (its exec path has the same defect as this one and is filed separately this run).
The exec path is the fallback taken exactly when the bounded warm path is unavailable or has failed — the moment a hang is most likely — and it is the one path with no bound.
Evidence (verified this run, main b2b885b9)
Sandbox probe with a fake muse binary ($HOME/.local/bin/muse under a scratch HOME; the fake answers --help probes with a flags line and sleeps 60s for any other invocation), MUSE_PROXY_WARM=0:
- Proxy log:
CLI probe: exec advertises 6 flags...thenSpawning: .../muse exec --json --prompt-file ... --model muse-spark-1.3 --reasoning-effort low --user-input-auto-resolve --approval-mode never— the request reached the exec spawn at:979. 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 — and a truly hung child never exits.
Dedup: gh searches for muse-proxy timeout and muse-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 codex-proxy exec path is filed separately this run.
Why it matters
This proxy is the only door from OpenCode sessions into the Meta pool (see the pool map in ~/hatch-personal/AGENTS.md), so a wedged exec turn stalls the session routed through it with no error, no log line, and no recovery short of the client dying — at which point the accumulated child is killed only as a side effect of the disconnect. A bounded fallback would surface the same stall as a prompt, actionable error, consistent with how the warm path already behaves at 300s.
Acceptance criteria
- A
muse execturn that exceeds a named deadline (consistent with the house convention — this file's ownWARM_TURN_TIMEOUT_MS300s, or a strictly parsedMUSE_PROXY_*env override) is terminated (SIGTERM, then SIGKILL after a short grace) and the request settles with the existing error-response shape (including the SSE error framing for streaming requests, muse-proxy: streaming execution failure is sent as unframed JSON on a 200 SSE response — clients see an empty successful stream #670). - The partially accumulated content / terminal-state reconciliation on
close(muse-proxy (PR #479): streaming responses silently drop the terminal text tail that non-streaming returns #484/muse-proxy: a run that fails after producing partial output is reported as a successful 'stop' — failure is masked whenever any content exists #672) still runs for a deadline-killed child, or the deadline path produces an equivalent explicit failure — never a silent successfulstop. - The existing client-disconnect kill (
:992-996) keeps working and races safely with the deadline kill. - Regression tests in
bin/muse-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; the warm path is unchanged. - Prompt-file cleanup (
fs.unlinkSync(promptFile)) happens on the deadline path as it does onclose.
Activity
developing-today commentedon Oct 29, 2026
CODE-1054
dezren39 commentedon Oct 29, 2026
🤖 Agent note · filed
musebinary (MUSE_PROXY_WARM=0) that the exec fallback path has no turn deadline — request unsettled at 8s, child never killed — while this file's warm path (300s), its rpc (30s) and even its --help probe (10s) are all bounded. Sibling defect in the codex-proxy exec path filed as codex-proxy: exec transports have no turn deadline — a hung codex exec child hangs the request forever and is never killed #2157.107 remaining items
dezren39 commentedon Nov 2, 2026
🤖 Agent note · picked up
dezren39 commentedon Nov 2, 2026
🤖 Agent note · parked
dezren39 commentedon Nov 2, 2026
🤖 Agent note · picked up + parked (queue task 5196, repo-worker-code, OpenChamber scheduled lane)
71c42421. Active Drew ownership unchanged.dezren39 commentedon Nov 2, 2026
🤖 Agent note · picked up
fix(muse-proxy): settle timed-out exec requests independently of chil…
Merge pull request #4242 from developing-today/queue/5196-2159-exec-t…
dezren39 commentedon Nov 2, 2026
🤖 Agent note · done