Skip to content

fix: handle SIGUSR1 restart on Windows where the signal is unsupported#84280

Open
Thatgfsj wants to merge 3 commits into
openclaw:mainfrom
Thatgfsj:fix/windows-sigusr1-restart
Open

fix: handle SIGUSR1 restart on Windows where the signal is unsupported#84280
Thatgfsj wants to merge 3 commits into
openclaw:mainfrom
Thatgfsj:fix/windows-sigusr1-restart

Conversation

@Thatgfsj

@Thatgfsj Thatgfsj commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

On Windows, process.kill(pid, "SIGUSR1") throws TypeError [ERR_UNKNOWN_SIGNAL]: Unknown signal: SIGUSR1 because SIGUSR1 is a Unix-only signal. This broke openclaw gateway restart for unmanaged gateway processes on Windows.

Changes

1. src/infra/gateway-processes.ts

Defensive guard: signalVerifiedGatewayPidSync throws a clear error when SIGUSR1 is used on win32, instead of crashing with an obscure Node.js ERR_UNKNOWN_SIGNAL.

2. src/cli/daemon-cli/lifecycle.ts

restartGatewayWithoutServiceManager now uses triggerOpenClawRestart() 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 run openclaw 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() uses schtasks /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"
  • Existing test pinned to linux platform for deterministic behavior
  • 27 of 28 tests pass (1 pre-existing failure: test hardcodes Linux 60s timeout, gets Windows 180s — unrelated to this change)

Real behavior proof

  • Behavior addressed: openclaw gateway restart crashes on Windows with ERR_UNKNOWN_SIGNAL: Unknown signal: SIGUSR1 for unmanaged gateway processes. The gateway restart handler called process.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:

    1. openclaw gateway restart --json (service restart path)
    2. openclaw gateway status (verify gateway healthy post-restart)
    3. npx vitest run src/infra/gateway-processes.test.ts src/cli/daemon-cli/lifecycle.test.ts
  • Evidence after fix:
    Service restart on Windows (gateway managed via Scheduled Task):

    $ openclaw gateway restart --json
    {
      "action": "restart",
      "ok": true,
      "result": "restarted",
      "service": {
        "label": "Scheduled Task",
        "loaded": true,
        "loadedText": "registered",
        "notLoadedText": "missing"
      }
    }
    
    $ openclaw gateway status
    Service: Scheduled Task (registered)
    Runtime: running (pid 23880, Verified gateway listener detected on port 18789)
    Connectivity probe: ok
    Capability: admin-capable
    Listening: 127.0.0.1:18789
    CLI version: 2026.5.18
    Gateway version: 2026.5.18
    

    Unit test results (28 tests, 27 passed, 1 pre-existing unrelated failure):

     Test Files  1 failed | 1 passed (2)
          Tests  1 failed | 27 passed (28)
    
    • New tests "rejects SIGUSR1 on Windows" and "uses scheduled task restart for unmanaged gateway restart on Windows" both pass
    • The 1 failure is pre-existing: "fails restart when gateway remains unhealthy after the full timeout" hardcodes Linux 60s timeout expectation; on real Windows the platform-aware timeout is 180s
  • Observed result after fix: Gateway restarts successfully on Windows. The defensive guard in signalVerifiedGatewayPidSync now throws a clear error message instead of Node.js's obscure ERR_UNKNOWN_SIGNAL. The unmanaged restart path uses triggerOpenClawRestart() 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.

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.
@openclaw-barnacle openclaw-barnacle Bot added cli CLI command changes size: S triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 19, 2026
@clawsweeper

clawsweeper Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 30, 2026, 5:41 AM ET / 09:41 UTC.

Summary
The PR changes Windows unmanaged gateway restart to guard SIGUSR1 and call the existing Scheduled Task restart handoff instead of sending SIGUSR1, with focused unit coverage.

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.

  • Windows unmanaged restart fallback changed: 1 fallback path changed. This branch decides how raw Windows gateway listeners restart when the managed service is missing, so it carries availability and upgrade risk beyond ordinary unit coverage.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🦪 silver shellfish
Patch quality: 🧂 unranked krab
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Protect the active raw gateway PID and preflight the Windows handoff target before any stale cleanup.
  • [P1] Add a regression test for a raw verified listener with no Scheduled Task or startup target.
  • [P1] Add redacted live Windows terminal or log proof for the unmanaged restart path after the code fix.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The PR body includes Windows terminal proof for the registered Scheduled Task service path, but it explicitly does not exercise the changed unmanaged Windows fallback after the patch. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Mantis proof suggestion
A real Windows desktop or terminal proof would verify the unmanaged Scheduled Task handoff that mocked tests and service-path proof do not cover. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis visual task: verify on Windows that openclaw gateway restart for a manually started unmanaged gateway does not kill the raw gateway unless a Scheduled Task or startup handoff is available.

Risk before merge

  • [P2] The new Windows unmanaged fallback can terminate the currently running raw gateway before a Scheduled Task or startup fallback is proven available.
  • [P2] Existing raw Windows users without a registered Scheduled Task may see restart behavior change from an immediate SIGUSR1 error to a managed-handoff requirement followed by timeout or process loss.
  • [P2] The supplied real behavior proof exercises the registered service restart path, not the changed unmanaged service-not-loaded fallback.

Maintainer options:

  1. Preflight and protect before handoff (recommended)
    Before calling the Scheduled Task handoff, prove a task or startup target exists and ensure stale cleanup cannot terminate the verified unmanaged PID being restarted.
  2. Align with Windows supervisor detection
    Pause this PR until fix(supervisor): probe schtasks directly when env vars are missing #95095 settles how env-missing Scheduled Task detection should work across Windows restart paths.
  3. Accept managed-handoff requirement
    Maintainers may intentionally require unmanaged Windows users to install the Scheduled Task, but that compatibility choice needs explicit ownership and live upgrade proof.

Next step before merge

  • [P1] Human follow-up is needed because the remaining blocker combines a concrete process-control defect with external unmanaged Windows real behavior proof that ClawSweeper automation cannot supply for the contributor.

Security
Cleared: No concrete security or supply-chain issue found; the diff changes local process-control flow and tests only, with no dependency, workflow, lockfile, secret, or third-party execution changes.

Review findings

  • [P1] Protect the raw gateway before the handoff — src/cli/daemon-cli/lifecycle.ts:222
Review details

Best 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:

  • [P1] Protect the raw gateway before the handoff — src/cli/daemon-cli/lifecycle.ts:222
    This new branch calls triggerOpenClawRestart() after finding a verified unmanaged listener PID. That helper first runs stale-gateway cleanup with no protected PID, and on Windows that cleanup can terminate the same listener before relaunchGatewayScheduledTask() has proven a Scheduled Task or startup fallback exists; preflight the managed target and protect pids[0], or use a handoff helper that cannot clean up the active unmanaged process.
    Confidence: 0.89

Overall correctness: patch is incorrect
Overall confidence: 0.89

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 738b2be4b49b.

Label changes

Label justifications:

  • P2: The PR targets a Windows-only gateway restart failure with clear source evidence and limited platform-specific blast radius.
  • merge-risk: 🚨 compatibility: Existing raw Windows gateway users without a registered Scheduled Task can see restart behavior require or assume a managed restart target.
  • merge-risk: 🚨 availability: The changed fallback can kill the current unmanaged gateway before a replacement process is confirmed.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🦪 silver shellfish and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The PR body includes Windows terminal proof for the registered Scheduled Task service path, but it explicitly does not exercise the changed unmanaged Windows fallback after the patch. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +25, Tests +44. Total +69 across 4 files.

View PR surface stats
Area Files Added Removed Net
Source 2 26 1 +25
Tests 2 44 0 +44
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 4 70 1 +69

Acceptance criteria:

  • [P1] Add or update focused unit coverage for the unmanaged Windows service-not-loaded path where no Scheduled Task/startup target is available.
  • [P1] Add or update focused unit coverage proving the active unmanaged PID is protected from stale cleanup before the handoff.
  • [P1] Require redacted live Windows proof: start a raw unmanaged gateway, run openclaw gateway restart --json, then show gateway status with healthy replacement behavior or a clean preflight failure without killing the raw process.

What I checked:

  • PR head unmanaged Windows branch: At PR head, restartGatewayWithoutServiceManager writes a restart intent, then on win32 calls triggerOpenClawRestart() instead of signaling the verified listener PID. (src/cli/daemon-cli/lifecycle.ts:221, fa95f44788ec)
  • Current main unmanaged restart behavior: Current main still writes a restart intent and sends SIGUSR1 to the single verified unmanaged gateway PID, so the PR is not obsolete on main. (src/cli/daemon-cli/lifecycle.ts:250, 738b2be4b49b)
  • Handoff cleanup runs before Windows relaunch: triggerOpenClawRestart() calls cleanStaleGatewayProcessesSync() before dispatching to relaunchGatewayScheduledTask() on Windows. (src/infra/restart.ts:692, 738b2be4b49b)
  • Cleanup can terminate the target listener: cleanStaleGatewayProcessesSync() finds verified Windows gateway listeners on the port and terminates them unless a protectedPid is supplied; the PR call supplies no protected PID. (src/infra/restart-stale-pids.ts:620, 738b2be4b49b)
  • Windows handoff returns before task proof: relaunchGatewayScheduledTask() writes and spawns a detached cmd helper and returns ok:true before that helper's schtasks query/run and fallback script prove a replacement gateway target exists. (src/infra/windows-task-restart.ts:80, 738b2be4b49b)
  • Node Windows signal contract: Node v22.19.0 process docs state SIGTERM is not supported on Windows and process.kill emulates SIGINT/SIGTERM/SIGKILL by unconditionally terminating the target process, supporting the PR's rejection of the earlier SIGTERM workaround. (nodejs/node/doc/api/process.md:780, eb7b9b3265d5)

Likely related people:

  • obviyus: Remote commit search and path history tie the Scheduled Task restart handoff this PR reuses to commit 05c240f. (role: introduced adjacent Windows Scheduled Task restart behavior; confidence: high; commits: 05c240fad6fe; files: src/infra/windows-task-restart.ts, src/infra/restart.ts, src/infra/process-respawn.ts)
  • mjamiv: Remote process-respawn history ties the ordinary unmanaged in-process fallback policy to commit c93d6d8, which is adjacent to this Windows restart decision. (role: introduced current unmanaged restart policy; confidence: high; commits: c93d6d8daa37; files: src/infra/process-respawn.ts, src/infra/process-respawn.test.ts)
  • vincentkoc: Remote history shows recent Windows handoff path work and stale PID protection work, including 7dd01d1 and 7c6ad23. (role: recent Windows restart and stale-cleanup contributor; confidence: high; commits: 7dd01d15c56d, 7c6ad2327c64; files: src/infra/windows-task-restart.ts, src/infra/restart-stale-pids.ts)
  • andyk-ms: Remote history shows adjacent Scheduled Task restart race work in commit 6d26609. (role: adjacent Windows task restart contributor; confidence: medium; commits: 6d26609f7baf; files: src/infra/windows-task-restart.ts)
  • Dallin Romney: Local blame on current main points the unmanaged lifecycle lines to commit 2ec6708, but that commit title is unrelated to this restart behavior, so routing confidence is low. (role: recent current-main carry-forward contributor; confidence: low; commits: 2ec670898018; files: src/cli/daemon-cli/lifecycle.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels May 19, 2026
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.
@Thatgfsj

Copy link
Copy Markdown
Contributor Author

@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 process.kill(pid, "SIGTERM"), so the gateway's JS-level SIGTERM handler never runs. The process would just die without restarting — which is exactly the availability risk you flagged.

I've reverted to using triggerOpenClawRestart() (the existing Windows scheduled-task restart handoff), consistent with how emitGatewayRestart() already handles the in-process Windows path at src/infra/restart.ts:615. The restart intent file is still written beforehand, and if the scheduled task is not installed, the error now clearly directs users to run openclaw gateway install. Thanks for the thorough review!

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 20, 2026
@clawsweeper

clawsweeper Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

🎁 Pass real behavior proof to wake the egg and unlock a hatchable treat.

Where did the egg go?
  • The egg game starts only after the PR passes the real-behavior proof check.
  • Before that, no creature or rarity is rolled. The treat waits for real proof.
  • This is still just collectible flavor: proof affects review readiness, not creature quality.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 21, 2026
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. label Jun 22, 2026
@clawsweeper

clawsweeper Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli CLI command changes merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: S status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant