fix(windows): prevent restart race from duplicate schtasks /Run#52487
fix(windows): prevent restart race from duplicate schtasks /Run#52487andyk-ms wants to merge 1 commit intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR fixes the "triple window" bug on Windows by inserting a pre-flight Key changes:
Notable concern:
Confidence Score: 4/5
Prompt To Fix All With AIThis 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 |
| `schtasks /Query /TN ${quotedTaskName} /FO LIST 2>nul | findstr /I "Running" >nul 2>&1`, | ||
| "if not errorlevel 1 goto cleanup", |
There was a problem hiding this 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.
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.There was a problem hiding this comment.
Addressed: the script uses PowerShell \Get-ScheduledTask\ whose .State\ property returns an English enum (\Running, \Ready, etc.) regardless of OS locale. The \indstr /I \Running\ here matches that PowerShell output, not \schtasks /Query\ output — so it's locale-safe.
There was a problem hiding this comment.
💡 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".
| @@ -27,6 +27,10 @@ function buildScheduledTaskRestartScript(taskName: string): string { | |||
| ":retry", | |||
| `timeout /t ${TASK_RESTART_RETRY_DELAY_SEC} /nobreak >nul`, | |||
| "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`, | |||
There was a problem hiding this comment.
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 👍 / 👎.
|
Addressed the locale-sensitivity feedback: replaced |
|
CI: The only failure is security-fast which appears to be an infra flake (not related to this PR's changes). Could a maintainer re-run the failed job? All other 45/46 checks pass. Thanks! |
ed7abae to
7895ec0
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7895ec0bac
ℹ️ 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".
| // Use PowerShell Get-ScheduledTask instead of schtasks /Query because | ||
| // schtasks outputs localized status strings on non-English Windows, | ||
| // while Get-ScheduledTask .State is always an English enum. | ||
| `powershell -Command "(Get-ScheduledTask -TaskName '${taskName}' -ErrorAction SilentlyContinue).State" 2>nul | findstr /I "Running" >nul 2>&1`, |
There was a problem hiding this comment.
Escape task name in PowerShell running-state probe
The new running check embeds taskName directly in -TaskName '${taskName}' inside a .cmd line, so values from OPENCLAW_WINDOWS_TASK_NAME (or profile-derived names) containing characters like ' or % are parsed incorrectly (' breaks the PowerShell string, % is expanded by cmd before PowerShell runs). In those cases the probe never detects an already-running task and the script still calls schtasks /Run, which can recreate the duplicate-start race this patch is trying to prevent.
Useful? React with 👍 / 👎.
|
Checked upstream main — no fixes landed for the failing \�xtension-signal\ / \�xtension-memory-core\ tests. These failures are pre-existing upstream flakes (Signal SSE stream timeout, memory module unexpected status) unrelated to the \Get-ScheduledTask\ change in this PR. The actual fix is solid. Could a maintainer re-run CI or confirm these are known flakes? Happy to rebase once upstream stabilises these tests. |
7895ec0 to
b0ef1ef
Compare
|
Rebased onto current main and addressed the P2 review comment: task name single quotes are now escaped by doubling ('') for safe PowerShell string interpolation. |
b0ef1ef to
ec1f20d
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ec1f20d6c7
ℹ️ 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".
| // Use PowerShell Get-ScheduledTask instead of schtasks /Query because | ||
| // schtasks outputs localized status strings on non-English Windows, | ||
| // while Get-ScheduledTask .State is always an English enum. | ||
| `powershell -Command "(Get-ScheduledTask -TaskName '${taskName.replace(/'/g, "''")}' -ErrorAction SilentlyContinue).State" 2>nul | findstr /I "Running" >nul 2>&1`, |
There was a problem hiding this comment.
Run scheduled-task probe without PowerShell profiles
The new running-state probe uses powershell -Command without -NoProfile/-NonInteractive, so user or system PowerShell profiles execute before Get-ScheduledTask. On machines with customized profiles, extra stdout (for example text containing Running) can make findstr report a false match, and interactive/profile startup code can block the probe entirely; both cases prevent the helper from reliably reaching schtasks /Run during self-restart.
Useful? React with 👍 / 👎.
|
Friendly ping — CI is all green. Would appreciate a review when you get a chance. Thanks! |
|
Another friendly ping — CI remains green and the fix is solid. Would appreciate a maintainer review when you get a chance. Thanks! |
|
Codex review: needs real behavior proof before merge. Summary Reproducibility: yes. Source inspection on current main shows the CLI restart path starts the scheduled task while the shutdown self-restart helper separately retries Real behavior proof Next step before merge Security Review detailsBest possible solution: Land this patch or an equivalent current-main port after adding redacted native Windows restart proof that one gateway instance remains, while preserving the existing fallback behavior and regression tests. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection on current main shows the CLI restart path starts the scheduled task while the shutdown self-restart helper separately retries Is this the best way to solve the issue? Yes for the code direction. The self-restart running-state guard is the narrowest maintainable fix for this duplicate What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 0071f67fe6ea. |
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
ec1f20d to
9a31bc8
Compare
|
Maintainer refresh: rebased this onto current Prepared head: Local verification passed:
I also added focused coverage for the duplicate |
Problem
When
openclaw gateway restartruns, two sources race to start the task:schtasks /Endthenschtasks /RunrelaunchGatewayScheduledTask()which spawns a detached.cmdthat retriesschtasks /Runup to 12 timesBoth succeed, creating duplicate gateway windows (the "triple window" bug).
Fix
The self-restart
.cmdscript now queries the task status before attemptingschtasks /Run. If the task is already running (CLI beat it there), the script exits cleanly instead of spawning a duplicate.Impact
Fixes #52044