fix: handle SIGUSR1 restart on Windows where the signal is unsupported#84280
fix: handle SIGUSR1 restart on Windows where the signal is unsupported#84280Thatgfsj wants to merge 3 commits into
Conversation
On Windows, `process.kill(pid, "SIGUSR1")` throws ERR_UNKNOWN_SIGNAL because SIGUSR1 is a Unix-only signal. This broke `openclaw gateway restart` for unmanaged gateway processes on Windows. Two changes: - signalVerifiedGatewayPidSync: throw a clear error when SIGUSR1 is used on win32 instead of crashing with an obscure Node.js error - restartGatewayWithoutServiceManager: on Windows, use the scheduled task restart mechanism (triggerOpenClawRestart) instead of sending SIGUSR1 directly Fixes the "Gateway restart failed: TypeError [ERR_UNKNOWN_SIGNAL]: Unknown signal: SIGUSR1" error on Windows.
|
Codex review: needs real behavior proof before merge. Reviewed June 30, 2026, 5:41 AM ET / 09:41 UTC. Summary PR surface: Source +25, Tests +44. Total +69 across 4 files. Reproducibility: yes. at source level: on the PR head, win32 plus service-not-loaded plus one verified gateway PID enters the new branch and calls triggerOpenClawRestart(), whose cleanup can terminate verified listeners without protecting that PID. I did not run a live Windows unmanaged gateway in this read-only review. Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Mantis proof suggestion Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Keep the SIGUSR1 Windows guard, but make the unmanaged Windows branch preflight a valid Scheduled Task or startup handoff and protect the active listener PID before cleanup, then require real unmanaged Windows proof. Do we have a high-confidence way to reproduce the issue? Yes, at source level: on the PR head, win32 plus service-not-loaded plus one verified gateway PID enters the new branch and calls triggerOpenClawRestart(), whose cleanup can terminate verified listeners without protecting that PID. I did not run a live Windows unmanaged gateway in this read-only review. Is this the best way to solve the issue? No as written: the Scheduled Task handoff is the right direction only after a managed target is proven and the active raw PID is protected. A narrower fix should fail before cleanup when no task or startup target exists, or use a handoff helper that cannot clean up the active unmanaged process. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 738b2be4b49b. Label changesLabel justifications:
Evidence reviewedPR surface: Source +25, Tests +44. Total +69 across 4 files. View PR surface stats
Acceptance criteria:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
On Windows, SIGUSR1 is not supported. The gateway's SIGTERM handler already reads the restart intent file and triggers a draining restart when the intent is present, rather than stopping. So for the unmanaged restart path on Windows, send SIGTERM (which is supported) instead of SIGUSR1. This is simpler than the triggerOpenClawRestart/schtasks approach and works without a scheduled task.
On Windows, neither SIGUSR1 nor SIGTERM are safe for cross-process restart signaling: SIGUSR1 is unsupported, and Node.js unconditionally terminates the target process on SIGTERM, bypassing any JS-level handler. Instead, use the existing triggerOpenClawRestart() path (schtasks-based handoff) which is the established Windows restart mechanism. If it fails (e.g. no scheduled task installed), the error message directs the user to run "openclaw gateway install". Per ClawSweeper P1 review feedback — the SIGTERM substitution was incorrect because: "Node v26 docs state that process.kill() with SIGTERM on Windows causes unconditional termination of the target process" so the gateway's SIGTERM handler would never run.
|
@clawsweeper — excellent catch on the SIGTERM approach. I did a deeper investigation and you're right: Node.js on Windows unconditionally terminates the target process with cross-process I've reverted to using |
|
ClawSweeper PR egg 🎁 Pass real behavior proof to wake the egg and unlock a hatchable treat. Where did the egg go?
|
|
@Thatgfsj thanks for the PR. ClawSweeper is still waiting on real behavior proof before this can move forward. Useful proof can be a screenshot, short video, terminal output, copied live output, linked artifact, or redacted logs that show the changed behavior after the fix. Please redact private tokens, phone numbers, private endpoints, customer data, and anything else sensitive. Once proof is added to the PR body or a comment, ClawSweeper or a maintainer can re-check it. |
Summary
On Windows,
process.kill(pid, "SIGUSR1")throwsTypeError [ERR_UNKNOWN_SIGNAL]: Unknown signal: SIGUSR1because SIGUSR1 is a Unix-only signal. This brokeopenclaw gateway restartfor unmanaged gateway processes on Windows.Changes
1.
src/infra/gateway-processes.tsDefensive guard:
signalVerifiedGatewayPidSyncthrows a clear error when SIGUSR1 is used on win32, instead of crashing with an obscure Node.jsERR_UNKNOWN_SIGNAL.2.
src/cli/daemon-cli/lifecycle.tsrestartGatewayWithoutServiceManagernow usestriggerOpenClawRestart()on win32 (the existing Windows scheduled-task restart handoff) instead of sending SIGUSR1. If the scheduled task is not installed, the error message directs users to runopenclaw gateway install.Why not SIGTERM? Per ClawSweeper review: Node.js on Windows unconditionally terminates the target process on cross-process SIGTERM, bypassing JS-level handlers. The gateway SIGTERM handler would never run, so the process would just die without restarting.
triggerOpenClawRestart()usesschtasks /Run+ a fallback cmd script, which is the established Windows restart path.Test plan
gateway-processes.test.ts: new test "rejects SIGUSR1 on Windows"lifecycle.test.ts: new test "uses scheduled task restart for unmanaged gateway restart on Windows"Real behavior proof
Behavior addressed:
openclaw gateway restartcrashes on Windows withERR_UNKNOWN_SIGNAL: Unknown signal: SIGUSR1for unmanaged gateway processes. The gateway restart handler calledprocess.kill(pid, "SIGUSR1")which is not a valid signal on Windows.Real environment tested: Windows 11 Pro (10.0.28000), OpenClaw 2026.5.18 (patched), Node.js 22.x, git for Windows bash shell
Exact steps or command run after the patch:
openclaw gateway restart --json(service restart path)openclaw gateway status(verify gateway healthy post-restart)npx vitest run src/infra/gateway-processes.test.ts src/cli/daemon-cli/lifecycle.test.tsEvidence after fix:
Service restart on Windows (gateway managed via Scheduled Task):
Unit test results (28 tests, 27 passed, 1 pre-existing unrelated failure):
Observed result after fix: Gateway restarts successfully on Windows. The defensive guard in
signalVerifiedGatewayPidSyncnow throws a clear error message instead of Node.js's obscureERR_UNKNOWN_SIGNAL. The unmanaged restart path usestriggerOpenClawRestart()which spawns the existing Windows schtasks-based restart mechanism, consistent with the in-process restart path.Not tested: Unmanaged gateway restart path (requires stopping the Windows Scheduled Task service first and running a raw gateway process). This code path is covered by the unit tests with mocked platform="win32" and mocked
triggerOpenClawRestart.