fix(infra): add error listener to spawnDetachedGatewayProcess to prevent crash on async spawn failure#101490
Conversation
…ent crash on async spawn failure
spawnDetachedGatewayProcess() calls child.unref() without attaching an
error listener. If spawn() fails asynchronously (ENOMEM, missing
executable), the ChildProcess emits an unhandled error event that
crashes the Node parent process. Add a no-op .on("error") handler
before unref() to prevent the unhandled exception.
Fixes openclaw#101458
|
Thanks for the context here. I swept through the related work, and this is now duplicate or superseded. Close as duplicate/superseded: this branch implements a plausible narrow fix, but #101489 is an open maintainer-labeled landing candidate for the same gateway respawn bug with the same runtime change, cleaner test structure, and successful relevant checks. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Canonical path: Converge review and landing on #101489, then let the linked issue close when that canonical fix merges. So I’m closing this here and keeping the remaining discussion on #101489. Review detailsBest possible solution: Converge review and landing on #101489, then let the linked issue close when that canonical fix merges. Do we have a high-confidence way to reproduce the issue? Yes. Current main lacks a ChildProcess Is this the best way to solve the issue? No for this branch as the landing path. The fix shape is correct, but the best maintainer path is to review and land the canonical maintainer-labeled PR rather than keep duplicate branches open. Security review: Security review cleared: The diff only adds a no-op child-process error listener and test updates, with no new dependency, secret, workflow, or permission surface. AGENTS.md: found and applied where relevant. What I checked:
Likely related people:
Codex review notes: model internal, reasoning high; reviewed against 24330a82b44a. |
|
Updated the PR body with before/after terminal output evidence showing the missing executable no longer crashes after the listener is attached. Requesting re-review. @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
Summary
Problem:
spawnDetachedGatewayProcess()callschild.unref()without an error listener. Ifspawn()fails asynchronously (ENOMEM, missing executable), theChildProcessemits an unhandlederrorevent that crashes the Node process.Solution: Add
child.on("error", () => {})beforechild.unref()to prevent the unhandled exception. The child is detached + unref'd — fire-and-forget, so a no-op handler is sufficient.What changed:
src/infra/process-respawn.ts:55-56— attach no-op error listener before unrefsrc/infra/process-respawn.test.ts— update mock children to includeon(); add test verifying error listener registrationWhat did NOT change: No behavioral change on success path. No config/env change. No user-visible behavior.
What Problem This Solves
When the gateway's update respawn logic calls
spawnDetachedGatewayProcess(), the spawned child process is detached and unreferenced for fire-and-forget execution. However, ifspawn()fails asynchronously — for example, due to OS resource exhaustion (ENOMEM) or the Node.js executable being removed during an update — theChildProcessemits anerrorevent. Without an error listener, Node.js throws an unhandled exception, crashing the parent gateway process and causing a denial of service.Root Cause
src/infra/process-respawn.ts:51-57:spawnDetachedGatewayProcess()returns the result ofchild_process.spawn()after calling.unref()but never attaches an.on("error")listener. Node.jsChildProcessis anEventEmitter; unhandlederrorevents throw and crash the process.Real behavior proof
Behavior addressed: Spawned child process now has a no-op error listener, preventing unhandled exception crashes on async spawn failure.
Real environment tested: Linux x64 (kernel 4.19.112), Node.js v24.13.1, OpenClaw main@48e77b6abf
Exact steps or command run after this patch:
Observed result after the fix: Missing executable no longer crashes the process; error is caught by the listener.
respawnGatewayProcessForUpdate()returns correctly in both the disabled and spawn paths.After-fix evidence:
What was not tested: Real ENOMEM/missing-executable-during-update conditions (requires OS resource exhaustion). End-to-end gateway restart through a real supervisor.
Risk checklist
Risk level: Low