fix(gateway): handle SIGUSR1 gracefully on Windows#69056
fix(gateway): handle SIGUSR1 gracefully on Windows#69056Thatgfsj wants to merge 2 commits intoopenclaw:mainfrom
Conversation
On Windows, process.kill(pid, "SIGUSR1") throws ERR_UNKNOWN_SIGNAL because SIGUSR1 is a Unix-only signal not supported by Node.js on Windows. This change detects the win32 platform in emitGatewayRestart() and calls triggerOpenClawRestart() directly instead, which already uses schtasks for Windows task scheduler restarts. The mode field in scheduleGatewaySigusr1Restart() is also updated so it reports "emit" instead of "signal" on Windows, correctly reflecting the fallback behaviour. Fixes the "Gateway restart failed: TypeError [ERR_UNKNOWN_SIGNAL]: Unknown signal: SIGUSR1" error when running openclaw gateway restart on Windows.
Greptile SummaryThis PR fixes
Confidence Score: 3/5Not safe to merge as-is; a schtasks failure silently deadlocks the Windows restart path. The Windows signal fix is correct in intent, but ignoring the RestartAttempt return value leaves a clear failure path where emittedRestartToken is never consumed on Windows, permanently blocking retries within the same process lifetime. src/infra/restart.ts — the emitGatewayRestart() Windows branch needs to inspect the return value of triggerOpenClawRestart() and roll back emittedRestartToken on failure. Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/infra/restart.ts
Line: 128-132
Comment:
**Failed restart silently reported as success on Windows**
`triggerOpenClawRestart()` returns a `RestartAttempt` with an `ok` field, but the return value is discarded. If `relaunchGatewayScheduledTask` fails (e.g. `schtasks` exits non-zero), `emitGatewayRestart()` still returns `true` and bumps `emittedRestartToken`. Because `markGatewaySigusr1RestartHandled()` is never called on Windows (no SIGUSR1 handler fires), `hasUnconsumedRestartSignal()` stays `true` for the rest of the process lifetime, permanently blocking all future restart attempts via `scheduleGatewaySigusr1Restart()`.
The return value should be checked, and the token should be rolled back on failure, mirroring what the `catch` block already does for thrown exceptions.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix(gateway): handle SIGUSR1 gracefully ..." | Re-trigger Greptile |
| if (process.platform === "win32") { | ||
| // On Windows, SIGUSR1 is not supported by Node.js process.kill(). | ||
| // Calling triggerOpenClawRestart() directly instead, which uses schtasks on win32. | ||
| // The result is intentionally ignored here — triggerOpenClawRestart() logs its own errors. | ||
| triggerOpenClawRestart(); |
There was a problem hiding this comment.
Failed restart silently reported as success on Windows
triggerOpenClawRestart() returns a RestartAttempt with an ok field, but the return value is discarded. If relaunchGatewayScheduledTask fails (e.g. schtasks exits non-zero), emitGatewayRestart() still returns true and bumps emittedRestartToken. Because markGatewaySigusr1RestartHandled() is never called on Windows (no SIGUSR1 handler fires), hasUnconsumedRestartSignal() stays true for the rest of the process lifetime, permanently blocking all future restart attempts via scheduleGatewaySigusr1Restart().
The return value should be checked, and the token should be rolled back on failure, mirroring what the catch block already does for thrown exceptions.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/restart.ts
Line: 128-132
Comment:
**Failed restart silently reported as success on Windows**
`triggerOpenClawRestart()` returns a `RestartAttempt` with an `ok` field, but the return value is discarded. If `relaunchGatewayScheduledTask` fails (e.g. `schtasks` exits non-zero), `emitGatewayRestart()` still returns `true` and bumps `emittedRestartToken`. Because `markGatewaySigusr1RestartHandled()` is never called on Windows (no SIGUSR1 handler fires), `hasUnconsumedRestartSignal()` stays `true` for the rest of the process lifetime, permanently blocking all future restart attempts via `scheduleGatewaySigusr1Restart()`.
The return value should be checked, and the token should be rolled back on failure, mirroring what the `catch` block already does for thrown exceptions.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Good catch - fixed in 9550bad. The return value is now checked and the cycle token is rolled back on ok:false, mirroring the existing catch block behavior.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a957bc0e43
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| // On Windows, SIGUSR1 is not supported by Node.js process.kill(). | ||
| // Calling triggerOpenClawRestart() directly instead, which uses schtasks on win32. | ||
| // The result is intentionally ignored here — triggerOpenClawRestart() logs its own errors. | ||
| triggerOpenClawRestart(); |
There was a problem hiding this comment.
Treat failed Windows restart handoff as a failed emit
In the new win32 branch, triggerOpenClawRestart() is called but its { ok: false } result is ignored, so emitGatewayRestart() still reports success and leaves the restart cycle marked as emitted. On Windows, any synchronous handoff failure (e.g. cmd.exe/task launch failure) now causes callers to believe restart succeeded, while later restart requests are coalesced behind the unconsumed cycle token and never retried. Please check the return value and roll back/return false when the handoff is not ok.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Thanks for the review. Addressed in 9550bad - checks the return value and rolls back the cycle token when ok:false, preventing silent failures.
If schtasks fails on Windows, triggerOpenClawRestart() returns { ok: false }.
Previously the return value was discarded, so emitGatewayRestart() would
report success and leave the restart cycle token as unconsumed, blocking
all future restart attempts. Now the result is checked and the token is
rolled back on failure, mirroring the existing catch block behavior.
Summary
openclaw gateway restartfails on Windows withTypeError [ERR_UNKNOWN_SIGNAL]: Unknown signal: SIGUSR1process.kill(pid, "SIGUSR1")throws on Windows because SIGUSR1 is a Unix-only signal not supported by Node.jsprocess.platform === "win32"inemitGatewayRestart()and calltriggerOpenClawRestart()directly instead, which already usesschtasksfor Windows task scheduler restartsscheduleGatewaySigusr1Restart()now reports"emit"mode on Windows instead of"signal", correctly reflecting the fallback behaviourTest plan
openclaw gateway restartcompletes without ERR_UNKNOWN_SIGNAL on Windows