fix(infra): add error listener to detached spawned gateway process to prevent crashes#101482
fix(infra): add error listener to detached spawned gateway process to prevent crashes#101482krissding wants to merge 2 commits into
Conversation
…prevent crashes When spawn() fails asynchronously (e.g., ENOENT, ENOMEM), the ChildProcess emits an 'error' event. Without a listener attached, Node.js treats this as an unhandled exception and crashes the parent process. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
Thanks for the context here. I swept through the related work, and this is now duplicate or superseded. Close as superseded: this PR is a focused and likely correct fix, but the maintainer-labeled PR at #101489 owns the same production change with stronger listener-before-unref regression coverage, so keeping both open creates duplicate landing work. Canonical path: Use #101489 as the canonical landing path for #101458 and close parallel duplicate PR branches. So I’m closing this here and keeping the remaining discussion on #101489 and #101458. Review detailsBest possible solution: Use #101489 as the canonical landing path for #101458 and close parallel duplicate PR branches. Do we have a high-confidence way to reproduce the issue? Yes, source-level reproduction is high confidence: current main lacks the ChildProcess error listener, and a Node v24.18.0 probe shows a missing detached executable exits with an unhandled error unless the listener is attached. I did not run a full OpenClaw gateway update respawn flow. Is this the best way to solve the issue? The one-line listener before unref is the right fix shape, matching sibling restart-helper behavior. This PR is no longer the best landing vehicle because the maintainer-labeled canonical PR carries the same production fix with stronger ordering coverage. Security review: Security review cleared: The diff only adds a ChildProcess error listener and test mocks; it does not introduce new commands, permissions, dependency sources, secrets handling, or supply-chain changes. AGENTS.md: found and applied where relevant. What I checked:
Likely related people:
Codex review notes: model internal, reasoning high; reviewed against 6799b5b3d444. |
|
🦞✅ Reason: structured ClawSweeper close marker: close-required (sha=e53738ec8771767a81b16499eaf1f7e16bc872e2) Closed:
|
What Problem This Solves
spawnDetachedGatewayProcess()insrc/infra/process-respawn.tscallschild_process.spawn()withdetached: truebut doesn't attach anerrorlistener before callingchild.unref(). Whenspawn()fails asynchronously (e.g., ENOENT, ENOMEM), theChildProcessemits an unhandlederrorevent that crashes the parent Node.js process.Fixes #101458
Why This Change Was Made
The fix adds a
child.on("error", () => {})handler betweenspawn()andunref()— the same pattern used insrc/process/exec.tsandpackages/agent-core/src/harness/env/kill-tree.tsfor async spawn error handling. This prevents the crashing unhandled 'error' event while preserving all existing behavior.Evidence
Before Fix — Unhandled crash
spawn('/nonexistent/binary', [], {detached: true, stdio: 'ignore'}) crashes the process:
Exit code: 1 (CRASH)
After Fix — Graceful handling
With
child.on('error', () => {})added, the same spawn failure is caught:Regression Test
Added test
"attaches an error listener to the detached child before unref to prevent crashes"insrc/infra/process-respawn.test.ts— verifies that.on("error", ...)is called on the spawned child before returning fromrespawnGatewayProcessForUpdate().Production Source
File:
src/infra/process-respawn.tsMerge Risk
Low — single line change in process cleanup path. The empty error handler matches the established pattern in
kill-tree.ts:runTaskkill()which already useschild.once("error", () => {})for detached spawn error swallowing.🤖 Generated with Claude Code