Skip to content

fix(infra): windows-task-restart fallback to startup entry when schtasks task is unregistered#58943

Merged
obviyus merged 4 commits into
openclaw:mainfrom
imechZhangLY:main
Apr 5, 2026
Merged

fix(infra): windows-task-restart fallback to startup entry when schtasks task is unregistered#58943
obviyus merged 4 commits into
openclaw:mainfrom
imechZhangLY:main

Conversation

@imechZhangLY

@imechZhangLY imechZhangLY commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

fix(infra): windows-task-restart fallback to startup entry when schtasks task is unregistered

Summary

  • Problem: relaunchGatewayScheduledTask() generated a restart script that only tried schtasks /Run in a 12-iteration retry loop. If the scheduled task was never registered (because schtasks /Create failed and installation fell back to a Startup folder entry), the script retried 12 times against a non-existent task and silently gave up, leaving the gateway dead.
  • Why it matters: On Windows machines where schtasks is denied (e.g. restricted enterprise environments, UAC policies), the gateway could never self-restart when user input /restart or modify configs of openclaw
  • What changed: The generated .cmd script now (1) pre-checks schtasks /Query /TN <taskName> and skips retries if the task doesn't exist, and (2) falls back to launching script which resolved by resolveTaskScriptPath() directly via start "" /b cmd.exe /d /c <path>, matching the startup folder path already used by schtasks.ts for install/stop/restart. resolveTaskScriptPath() is now imported from schtasks.ts to resolve the script path consistently.
  • What did NOT change (scope boundary):

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #
  • Related #
  • This PR fixes a bug or regression

Root Cause / Regression History (if applicable)

For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write N/A. If the cause is unclear, write Unknown.

  • Root cause:
  • Missing detection / guardrail:
  • Prior context (git blame, prior PR, issue, or refactor if known):
  • Why this regressed now:
  • If unknown, what was ruled out:

Regression Test Plan (if applicable)

For bug fixes or regressions, name the smallest reliable test coverage that should have caught this. Otherwise write N/A.

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • [] End-to-end test
    • Existing coverage already sufficient
  • Target test or file:
  • Scenario the test should lock in:
  • Why this is the smallest reliable guardrail:
  • Existing test that already covers this (if any):
  • If no new test is added, why not:

User-visible / Behavior Changes

List user-visible changes (including defaults/config).
If none, write None.

Diagram (if applicable)

For UI changes or non-trivial logic flows, include a small ASCII diagram reviewers can scan quickly. Otherwise write N/A.

Before:
[user action] -> [old state]

After:
[user action] -> [new state] -> [result]

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: Windows
  • Runtime/container:
  • Model/provider:
  • Integration/channel (if any):
  • Relevant config (redacted):

Steps

  1. run openclaw onboard --install-daemon without UAC
  2. start gateway with set env varible OPENCLAW_WINDOWS_TASK_NAME
  3. input /restart to restart gateway

Expected

gateway was restared

Actual

gateway was stopped without restarting

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios:
  1. run openclaw onboard --install-daemon without UAC and then input /restart in webchat
  2. run openclaw onboard --install-daemon with UAC and then input /restart in webchat
  • Edge cases checked:
  • What you did not verify:

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes/No)
  • Config/env changes? (Yes/No)
  • Migration needed? (Yes/No)
  • If yes, exact upgrade steps:

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk:
    • Mitigation:

@greptile-apps

greptile-apps Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a real regression on Windows machines where schtasks /Create is denied (restricted enterprise environments): the generated restart helper .cmd script now pre-checks whether the scheduled task is actually registered before entering the retry loop, and falls back to launching gateway.cmd directly if the task doesn't exist or all retries are exhausted. The logic in the generated batch script is correct — goto cleanup on success still bypasses :fallback, and the if exist guard is appropriate.

Key changes:

  • buildScheduledTaskRestartScript adds a schtasks /Query /TN <task> pre-check; if the task is unregistered it jumps immediately to :fallback instead of looping 12× against a non-existent task.
  • Retry exhaustion now goto fallback instead of goto cleanup, so a registered-but-non-runnable task also falls back to the script launch path.
  • resolveTaskScriptPath is imported from schtasks.ts and used consistently in both production code and tests.
  • The fallback launch (start \"\" /b cmd.exe /d /c <path>) uses /b (same console, no new window), which is inconsistent with buildStartupLauncherScript in schtasks.ts that uses /min (new minimised window, fully detached console) and with launchFallbackTaskScript that uses spawn(..., { detached: true, windowsHide: true }). This subtle difference means the gateway process shares the helper script's hidden console and could receive a CTRL_CLOSE_EVENT when the helper exits.
  • The new test covers the fallback path but its assertions are loose and do not lock in the schtasks /Query pre-check behaviour.

Confidence Score: 4/5

Safe to merge with low risk; the core fix is sound but the /b vs /min process-detachment inconsistency warrants a second look before closing the loop on the fallback reliability.

The primary fix (pre-check + fallback label routing) is logically correct and addresses the stated bug. No P0/P1 issues found. One P2 concern: start /b shares the parent console with the restart helper, which is inconsistent with how every other callsite (buildStartupLauncherScript, launchFallbackTaskScript) achieves process isolation. Replacing /b with /min would align with the existing pattern. The test quality is also slightly weak (loose "start" assertion, missing query pre-check assertion), but that alone would not block merge.

src/infra/windows-task-restart.ts — the start /b flag in the fallback launch command (line 46) should be reviewed for process-detachment correctness.

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

Comment:
**`start /b` shares the parent console instead of detaching**

The fallback uses `start "" /b cmd.exe /d /c <path>`, which starts the gateway script *within the same hidden console* as the restart helper. When the helper script exits and its `cmd.exe` closes, Windows sends a `CTRL_CLOSE_EVENT` to all processes attached to that console – including the gateway launched with `/b`.

For reference, `buildStartupLauncherScript` in `schtasks.ts` uses `/min` precisely to create a new, separate console window:
```ts
lines.push(`start "" /min cmd.exe /d /c ${quoteCmdScriptArg(params.scriptPath)}`);
```
And `launchFallbackTaskScript` achieves the same isolation with `spawn(..., { detached: true, windowsHide: true })`.

Consider using `/min` here to match the same detachment semantics:
```suggestion
      `  start "" /min cmd.exe /d /c ${quotedScript}`,
```
This minimises the risk of the gateway receiving a console-close signal when the helper script terminates.

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

---

This is a comment left during a code review.
Path: src/infra/windows-task-restart.test.ts
Line: 177-179

Comment:
**Weak assertions in startup fallback test**

The test verifies `:fallback`, `"start"`, and the script path, but `"start"` would also match partial words (e.g. if future lines contain `"restart"` or `"setlocal"`). More importantly, the test does not assert the `schtasks /Query` pre-check (the other key change in this PR) or the exact launch command.

Consider strengthening to:
```ts
expect(script).toContain(`schtasks /Query /TN`);
expect(script).toContain(":fallback");
expect(script).toContain(`start "" /b cmd.exe /d /c`);
expect(script).toContain(taskScriptPath);
```
This would lock in the full behaviour introduced by this fix.

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

Reviews (1): Last reviewed commit: "fix code style problem" | Re-trigger Greptile

Comment thread src/infra/windows-task-restart.ts Outdated
Comment thread src/infra/windows-task-restart.test.ts
@imechZhangLY

Copy link
Copy Markdown
Contributor Author

Hi @obviyus . I’ve just submitted this PR. Could you please help take a look when you have time? Thanks a lot!

@obviyus obviyus left a comment

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.

Reviewed latest changes; landing now.

@obviyus obviyus left a comment

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.

Reviewed latest changes; landing now.

@obviyus
obviyus merged commit 0e61a1d into openclaw:main Apr 5, 2026
9 checks passed
@obviyus

obviyus commented Apr 5, 2026

Copy link
Copy Markdown
Contributor

Landed on main.

Thanks @imechZhangLY.

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

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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e212c6f6e9

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


export function relaunchGatewayScheduledTask(env: NodeJS.ProcessEnv = process.env): RestartAttempt {
const taskName = resolveWindowsTaskName(env);
const taskScriptPath = resolveTaskScriptPath(env);

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 Badge Move fallback script-path resolution inside guarded try block

relaunchGatewayScheduledTask now resolves resolveTaskScriptPath(env) before entering the existing try/catch, so a missing HOME/USERPROFILE (or other path-resolution error) will throw out of this function instead of returning a structured { ok: false } restart result. This is a regression from the previous behavior and can make /restart crash on Windows environments with stripped service env vars, because resolveTaskScriptPath delegates to state-dir resolution that throws when home variables are absent.

Useful? React with 👍 / 👎.

lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
…enclaw#58943) (thanks @imechZhangLY)

* fix(infra): windows-task-restart fallback to startup entry when schtasks task is unregistered

* fix code style problem

* use /min for startup fallback and assert schtasks pre-check in test

* fix: windows restart fallback when scheduled task is unregistered (openclaw#58943) (thanks @imechZhangLY)

---------

Co-authored-by: Luyao Zhang <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…enclaw#58943) (thanks @imechZhangLY)

* fix(infra): windows-task-restart fallback to startup entry when schtasks task is unregistered

* fix code style problem

* use /min for startup fallback and assert schtasks pre-check in test

* fix: windows restart fallback when scheduled task is unregistered (openclaw#58943) (thanks @imechZhangLY)

---------

Co-authored-by: Luyao Zhang <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…enclaw#58943) (thanks @imechZhangLY)

* fix(infra): windows-task-restart fallback to startup entry when schtasks task is unregistered

* fix code style problem

* use /min for startup fallback and assert schtasks pre-check in test

* fix: windows restart fallback when scheduled task is unregistered (openclaw#58943) (thanks @imechZhangLY)

---------

Co-authored-by: Luyao Zhang <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
…enclaw#58943) (thanks @imechZhangLY)

* fix(infra): windows-task-restart fallback to startup entry when schtasks task is unregistered

* fix code style problem

* use /min for startup fallback and assert schtasks pre-check in test

* fix: windows restart fallback when scheduled task is unregistered (openclaw#58943) (thanks @imechZhangLY)

---------

Co-authored-by: Luyao Zhang <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
Nachx639 pushed a commit to Nachx639/clawdbot that referenced this pull request Jun 17, 2026
…enclaw#58943) (thanks @imechZhangLY)

* fix(infra): windows-task-restart fallback to startup entry when schtasks task is unregistered

* fix code style problem

* use /min for startup fallback and assert schtasks pre-check in test

* fix: windows restart fallback when scheduled task is unregistered (openclaw#58943) (thanks @imechZhangLY)

---------

Co-authored-by: Luyao Zhang <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
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.

2 participants