Skip to content

fix(gateway): handle SIGUSR1 gracefully on Windows#69056

Open
Thatgfsj wants to merge 2 commits intoopenclaw:mainfrom
Thatgfsj:fix/windows-sigusr1
Open

fix(gateway): handle SIGUSR1 gracefully on Windows#69056
Thatgfsj wants to merge 2 commits intoopenclaw:mainfrom
Thatgfsj:fix/windows-sigusr1

Conversation

@Thatgfsj
Copy link
Copy Markdown

Summary

  • Bug: openclaw gateway restart fails on Windows with TypeError [ERR_UNKNOWN_SIGNAL]: Unknown signal: SIGUSR1
  • Root cause: process.kill(pid, "SIGUSR1") throws on Windows because SIGUSR1 is a Unix-only signal not supported by Node.js
  • Fix: Detect process.platform === "win32" in emitGatewayRestart() and call triggerOpenClawRestart() directly instead, which already uses schtasks for Windows task scheduler restarts
  • Also fixed: scheduleGatewaySigusr1Restart() now reports "emit" mode on Windows instead of "signal", correctly reflecting the fallback behaviour

Test plan

  • Verify openclaw gateway restart completes without ERR_UNKNOWN_SIGNAL on Windows
  • Verify restart still works correctly on Linux/macOS

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-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 19, 2026

Greptile Summary

This PR fixes openclaw gateway restart on Windows by routing around SIGUSR1 (unsupported by Node.js on Windows) and calling triggerOpenClawRestart() directly, and corrects the mode reporting in scheduleGatewaySigusr1Restart() to reflect the fallback behaviour.

  • P1: In emitGatewayRestart(), the return value of triggerOpenClawRestart() is silently ignored. If the Windows task-scheduler restart fails, emittedRestartToken is left permanently unconsumed, causing every subsequent call to scheduleGatewaySigusr1Restart() to bail out via hasUnconsumedRestartSignal() — effectively deadlocking all further restart attempts for the life of the process.

Confidence Score: 3/5

Not 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 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.

Reviews (1): Last reviewed commit: "fix(gateway): handle SIGUSR1 gracefully ..." | Re-trigger Greptile

Comment thread src/infra/restart.ts Outdated
Comment on lines +128 to +132
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();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/infra/restart.ts Outdated
// 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();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant