Skip to content

fix(windows): prevent restart race from duplicate schtasks /Run#52487

Open
andyk-ms wants to merge 2 commits intoopenclaw:mainfrom
andyk-ms:fix/windows-restart-race
Open

fix(windows): prevent restart race from duplicate schtasks /Run#52487
andyk-ms wants to merge 2 commits intoopenclaw:mainfrom
andyk-ms:fix/windows-restart-race

Conversation

@andyk-ms
Copy link
Copy Markdown
Contributor

Problem

When openclaw gateway restart runs, two sources race to start the task:

  1. CLI: schtasks /End then schtasks /Run
  2. Self-restart script: dying gateway fires relaunchGatewayScheduledTask() which spawns a detached .cmd that retries schtasks /Run up to 12 times

Both succeed, creating duplicate gateway windows (the "triple window" bug).

Fix

The self-restart .cmd script now queries the task status before attempting schtasks /Run. If the task is already running (CLI beat it there), the script exits cleanly instead of spawning a duplicate.

Impact

  • Zero behavior change for config-watcher restarts (task won't be running yet)
  • Prevents duplicate window on CLI-initiated restarts
  • Self-cleaning: script still deletes itself on exit

Fixes #52044

When CLI runs 'openclaw gateway restart', it does schtasks /End then
schtasks /Run. Meanwhile the dying gateway fires a self-restart script
(relaunchGatewayScheduledTask) that also retries schtasks /Run up to
12 times. Both succeed, creating duplicate gateway windows.

Fix: the self-restart .cmd script now checks if the task is already
running before attempting schtasks /Run. If another restart (CLI) beat
it there, the script exits cleanly instead of spawning a duplicate.

Fixes openclaw#52044
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 22, 2026

Greptile Summary

This PR fixes the "triple window" bug on Windows by inserting a pre-flight schtasks /Query status check into the self-restart .cmd script, preventing it from launching a duplicate gateway process when the CLI has already restarted the scheduled task.

Key changes:

  • Before each schtasks /Run attempt, the script now queries the task's current status and exits (goto cleanup) if the task is already in the Running state.
  • The fix is zero-impact for config-watcher-initiated restarts (task won't be running when the script first checks) and self-cleaning (script still deletes itself on exit).

Notable concern:

  • The findstr /I "Running" match against schtasks /Query /FO LIST output is locale-sensitive — on non-English Windows, the status string is localised (e.g. German, French), so the guard silently does nothing and the duplicate-window race can still occur for those users. Consider using (Get-ScheduledTask ...).State via PowerShell for a locale-agnostic check.

Confidence Score: 4/5

  • Safe to merge for English Windows users; non-English users won't regress but also won't benefit from the fix.
  • The fix is logically correct — the errorlevel semantics with the pipe are sound, the task names don't introduce false positives, and the failure mode on non-English Windows is benign (silent no-op, not a crash or new bug). The one actionable gap is locale sensitivity of the findstr "Running" check, which limits the fix's effectiveness to English Windows installations. That's worth addressing before this is considered fully resolved, but it doesn't break anything new.
  • src/infra/windows-task-restart.ts — specifically the findstr /I "Running" status check on line 32.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/infra/windows-task-restart.ts
Line: 32-33

Comment:
**`findstr "Running"` is locale-sensitive**

`schtasks /Query /FO LIST` outputs localised status strings on non-English Windows installations (e.g. German "Wird ausgeführt", French "En cours d'exécution"). On those systems `findstr /I "Running"` will never match, so the early-exit guard silently does nothing and the script falls back to the original retry-12-times behaviour — leaving the duplicate-window bug present for non-English users.

A locale-independent alternative is to use PowerShell's `Get-ScheduledTask`, whose `.State` property is always an English enum value regardless of OS locale:

```
`powershell -Command "(Get-ScheduledTask -TaskName ${quotedTaskName} -ErrorAction SilentlyContinue).State" 2>nul | findstr /I "Running" >nul 2>&1`,
```

Or, if PowerShell is undesirable, check the exit code of `schtasks /Run` for the specific HRESULT `0x41301` ("task is already running") rather than querying status first — though that code isn't easily testable from a plain batch script.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(windows): prevent restart race from ..." | Re-trigger Greptile

Comment on lines +32 to +33
`schtasks /Query /TN ${quotedTaskName} /FO LIST 2>nul | findstr /I "Running" >nul 2>&1`,
"if not errorlevel 1 goto cleanup",
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.

P2 findstr "Running" is locale-sensitive

schtasks /Query /FO LIST outputs localised status strings on non-English Windows installations (e.g. German "Wird ausgeführt", French "En cours d'exécution"). On those systems findstr /I "Running" will never match, so the early-exit guard silently does nothing and the script falls back to the original retry-12-times behaviour — leaving the duplicate-window bug present for non-English users.

A locale-independent alternative is to use PowerShell's Get-ScheduledTask, whose .State property is always an English enum value regardless of OS locale:

`powershell -Command "(Get-ScheduledTask -TaskName ${quotedTaskName} -ErrorAction SilentlyContinue).State" 2>nul | findstr /I "Running" >nul 2>&1`,

Or, if PowerShell is undesirable, check the exit code of schtasks /Run for the specific HRESULT 0x41301 ("task is already running") rather than querying status first — though that code isn't easily testable from a plain batch script.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/windows-task-restart.ts
Line: 32-33

Comment:
**`findstr "Running"` is locale-sensitive**

`schtasks /Query /FO LIST` outputs localised status strings on non-English Windows installations (e.g. German "Wird ausgeführt", French "En cours d'exécution"). On those systems `findstr /I "Running"` will never match, so the early-exit guard silently does nothing and the script falls back to the original retry-12-times behaviour — leaving the duplicate-window bug present for non-English users.

A locale-independent alternative is to use PowerShell's `Get-ScheduledTask`, whose `.State` property is always an English enum value regardless of OS locale:

```
`powershell -Command "(Get-ScheduledTask -TaskName ${quotedTaskName} -ErrorAction SilentlyContinue).State" 2>nul | findstr /I "Running" >nul 2>&1`,
```

Or, if PowerShell is undesirable, check the exit code of `schtasks /Run` for the specific HRESULT `0x41301` ("task is already running") rather than querying status first — though that code isn't easily testable from a plain batch script.

How can I resolve this? If you propose a fix, please make it concise.

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

ℹ️ 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".

"set /a attempts+=1",
// Check if the task is already running (e.g. CLI restart beat us to it).
// schtasks /Query output contains "Running" when the task is active.
`schtasks /Query /TN ${quotedTaskName} /FO LIST 2>nul | findstr /I "Running" >nul 2>&1`,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid locale-dependent running check in restart helper

This findstr /I "Running" guard depends on English schtasks output, but Windows localizes task status text, so on non-English installs the helper will miss the "already running" case and still execute /Run, allowing the duplicate-start race this patch is meant to prevent. Use a locale-invariant signal (for example Last Run Result code 0x41301) instead of matching an English token in the raw query output.

Useful? React with 👍 / 👎.

@andyk-ms
Copy link
Copy Markdown
Contributor Author

Addressed the locale-sensitivity feedback: replaced schtasks /Query /FO LIST | findstr /I Running with PowerShell's Get-ScheduledTask. The .State property returns an English enum (Running, Ready, etc.) regardless of OS locale, unlike schtasks which outputs localized status strings on non-English Windows.

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.

Bug: gateway restart spawns duplicate processes on Windows (3 windows)

2 participants