[AI] fix(update-cli): warn+continue on Windows schtasks access-denied#111814
[AI] fix(update-cli): warn+continue on Windows schtasks access-denied#111814xydt-tanshanshan wants to merge 1 commit into
Conversation
When schtasks /Change /DISABLE fails with a Windows access-denied error (reported in localized text such as 'ERROR: Access is denied.', '错误: 拒绝访问。', or 'Zugriff verweigert'), the package update no longer hard-exits. The task remains in its prior enabled state and the update proceeds with a warning telling the user to run 'openclaw gateway restart' from an elevated shell if the gateway fails to restart after the update. Other schtasks disable failures (timeouts, malformed output, etc.) stay fatal so real environmental problems are not masked. Related to openclaw#111756
|
Codex review: needs real behavior proof before merge. Reviewed July 20, 2026, 7:55 AM ET / 11:55 UTC. Summary PR surface: Source +32, Tests +73. Total +105 across 2 files. Reproducibility: yes. in source: the proposed catch receives any rejection from the single suspend promise, and the new matcher has no operation discriminator. A real Windows access-denied task was not supplied, so runtime proof for the intended branch remains missing. Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Copy recommended automerge instructionNext step before merge
Security Review findings
Review detailsBest possible solution: Represent the Do we have a high-confidence way to reproduce the issue? Yes in source: the proposed catch receives any rejection from the single suspend promise, and the new matcher has no operation discriminator. A real Windows access-denied task was not supplied, so runtime proof for the intended branch remains missing. Is this the best way to solve the issue? No. Catching a generic localized error message at the outer lifecycle boundary is not the narrowest fix; the implementation should identify the failed Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 0ac7b24d56ca. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +32, Tests +73. Total +105 across 2 files. View PR surface stats
Acceptance criteria:
What I checked:
Likely related people:
What the crustacean ranks mean
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
|
Summary
Windows package updates no longer hard-fail when
schtasks /Change /DISABLEreports an access-denied error; the update now warns and continues, leaving the task in its prior enabled state.openclaw updateon Windows hard-exits withFailed to stop managed gateway service before update: Error: schtasks disable failed: 错误: 拒绝访问。whenever the current shell lacks permission to disable the OpenClaw Scheduled Task (task owned by another identity or an elevated shell required), blocking the update entirely.maybeSuspendWindowsTaskAutoStartForPackageUpdate, detect access-denied failures (localizedAccess is denied/拒绝访问/Zugriff verweigert) and degrade to a warn+continue path instead of re-throwing. Other schtasks failures stay fatal.src/cli/update-cli/update-command-service.ts(addedWINDOWS_ACCESS_DENIED_PATTERNS+isWindowsAccessDeniedErrorhelper + access-denied branch in the catch block),src/cli/update-cli.test.ts(added 4 regression tests covering en/zh/de localizations plus a non-access failure that must still hard-fail)./Queryfailure handling (still fail-closed via existing tests),changeScheduledTaskEnabledStateitself, thearmWindowsTaskAutoStartRecoverysignal/restore plumbing, the resume path, the package-install execution path, non-Windows platforms, git-update path, service.stop() invocation, exit/abort semantics for non-access failures.Change Type
Scope
Linked Issue/PR
Motivation
Issue #111756 reports
openclaw update 2026.7.1failing on Windows 11 withschtasks disable failed: 错误: 拒绝访问。. The error is raised bychangeScheduledTaskEnabledStateinsrc/daemon/schtasks.ts:1844-1846whenschtasks /Change /TN <task> /DISABLEreturns a non-zero exit code, and is re-thrown bymaybeSuspendWindowsTaskAutoStartForPackageUpdate. The catch block inexecuteMutableUpdatethen logsFailed to stop managed gateway service before updateand callsdefaultRuntime.exit(1), aborting the update before the package install can run.The actual failure is a user-permission gap, not a code defect: schtasks
/Changerequires permission to modify the task, and an npm-installed OpenClaw whose Scheduled Task was registered by another identity (or an elevated shell) cannot be disabled from a non-elevated user shell. Hard-failing here blocks the update entirely with no recourse short of re-running as Administrator. The task remains in its prior enabled state when/Change /DISABLEfails, so suspending autostart was never going to succeed; continuing the update is safe and matches theimpact:ux-frictionsignal on the issue.Real behavior proof
openclaw updatecontinues with a warning whenschtasks /Change /DISABLEreturns an access-denied error, instead of hard-exiting.fix/schtasks-disable-access-denied-111756, Vitest 4.1.10 with existingvi.mock-based Windows schtasks simulation insrc/cli/update-cli.test.ts.node scripts/run-vitest.mjs src/cli/update-cli.test.ts -t "Scheduled Task disable" --run node scripts/run-vitest.mjs src/cli/update-cli.test.ts --run node scripts/run-vitest.mjs src/daemon/schtasks.stop.test.ts --run node_modules/.bin/oxlint src/cli/update-cli/update-command-service.ts src/cli/update-cli.test.ts node_modules/.bin/oxfmt --write src/cli/update-cli/update-command-service.ts src/cli/update-cli.test.ts node_modules/.bin/tsgo -p tsconfig.core.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core.tsbuildinfoMatrix-style evidence for the catch-block branches (input form → result):
/Change /DISABLEfails with an access-denied message in any of the three localized forms, the update no longer callsdefaultRuntime.exit(1); the package install proceeds,resumeScheduledTaskAutoStartAfterUpdateis not invoked (since no suspend actually succeeded), and the warning log names the failure detail and theopenclaw gateway restartrecovery command./Change /DISABLEfails for any other reason (e.g. timeout, malformed output), the prior hard-fail behavior is preserved:defaultRuntime.exit(1)is invoked, no package install runs, and the error log retains the originalFailed to stop managed gateway service before updatemessage.src/infra/boundary-file-read.tstsgo errors present during local typecheck exist onorigin/mainbaseline (confirmed viagit stash+ re-run) and are unrelated to this change.Root Cause
schtasks /Change /TN <task> /DISABLEreturns a non-zero exit code with locale-localized stderr (e.g.ERROR: Access is denied./错误: 拒绝访问。/Zugriff verweigert) when the calling shell cannot modify the task.changeScheduledTaskEnabledState(src/daemon/schtasks.ts:1844-1846) throwsschtasks disable failed: <detail>, whichmaybeSuspendWindowsTaskAutoStartForPackageUpdatere-throws, andexecuteMutableUpdate(src/cli/update-cli/update-command-execution.ts:100-108) catches by callingdefaultRuntime.exit(1)and throwingUpdateCommandAbort. The user-permission gap is treated identically to a real schtasks malfunction, blocking the entire package update.Confidence: clear. The throw site, catch site, and exit path are all directly cited in source.
Regression Test Plan
Four new
itcases added tosrc/cli/update-cli.test.tsinside the existingupdate-clidescribe block, exercising themaybeSuspendWindowsTaskAutoStartForPackageUpdatecatch branch through the publicupdateCommandentry point:continues a no-restart package update when Windows Scheduled Task disable reports access denied (en)— assertdefaultRuntime.exitnot called with 1, package install runs, resume not called, warn log contains detail +openclaw gateway restart.zh(错误: 拒绝访问。).de(Zugriff verweigert).still hard-fails a no-restart package update when Windows Scheduled Task disable fails for a non-access reason— assertdefaultRuntime.exitcalled with 1, package install never runs, resume not called, error log containsFailed to stop managed gateway service before update+ the original detail.The existing
schtasks.stop.test.tssuite (20 tests) continues to cover the lower-levelchangeScheduledTaskEnabledStatethrow paths and the/Queryfail-closed behavior, ensuring the access-denied branch only relaxes the/Change /DISABLEfailure mode.User-visible / Behavior Changes
Could not disable the Windows Scheduled Task before update ... Run \openclaw gateway restart` from an elevated shell if the gateway fails to restart after update.) instead of aborting withFailed to stop managed gateway service before update: Error: schtasks disable failed: ` and exit code 1.Security Impact
The change only relaxes the failure handling for one specific class of schtasks error; no new schtasks invocations, no new file paths, no new env vars, no new permissions.
Repro + Verification
Environment
Steps
openclaw update 2026.7.1.schtasks disable failed: 错误: 拒绝访问。and the subsequentFailed to stop managed gateway service before updateerror.Expected
After this fix: a warning
Could not disable the Windows Scheduled Task before update ...is logged and the package update proceeds.Actual
Before this fix:
openclaw updateexits with code 1 before the package install runs.Evidence
Human Verification
schtasks.stop.test.ts), task-absent path (pre-existing coverage), COM-proved absence path (pre-existing coverage), suspension-signal path (pre-existing coverage inupdate-cli.test.ts)./Change /DISABLEon an actual access-denied task; locales beyond en/zh/de; behavior under antivirus interference that produces a non-access-denied stderr.Compatibility / Migration
No config, env, or storage surface changes. Existing schtasks failures that are not access-denied continue to hard-fail exactly as before. The new pattern list is internal to
update-command-service.tsand grows only when a real report surfaces a new locale.Best-fix Verdict
Is this change the best way to solve the problem?
maybeSuspendWindowsTaskAutoStartForPackageUpdate) where the Windows-specific package-update suspension is decided, not at the lower-levelchangeScheduledTaskEnabledState(which would change behavior for/Querypaths and other callers). The pattern-based locale detection is anchored to observed real-world localizations (issue [Bug]: openclaw update failed - SQLite state has conflicting #111756 + existing test fixtures usingAccess is deniedandZugriff verweigert), not speculative, and the comment instructs future contributors to extend only on real reports. Returningundefinedreuses the existing "no suspension happened" semantics already used by the!suspendedbranch, so no new recovery plumbing is needed./Change /DISABLEfailure (not just access-denied). Rejected because it would mask real environmental problems (antivirus locks, schtasks binary corruption) that the existingfails closed on an ambiguous task querytest philosophy explicitly guards against.exit(1). Rejected because the issue'simpact:ux-frictionlabel signals the user is blocked, and a friendlier failure does not unblock the update.AI Assistance
glm-5.2(openai-compatible/glm-5.2)open-source-prskill was loaded, Step 1-7 workflow followed, Decision Gate 1 (issue acceptance) and Decision Gate 2 (technical plan: access-denied → warn+continue) confirmed by the user before implementation. The user explicitly chose Plan A (precise access-denied detection) over Plan B (any disable failure → continue) and Plan C (cosmetic message change).Risks and Mitigations
schtasks /Runis invoked while the package install is replacing files. In practice this is low: the user is already logged in (LogonTrigger already fired) andmaybeStopManagedServiceBeforeMutableUpdateseparately callsservice.stop()when the gateway is running, which terminates the process directly without relying on the disable bit.openclaw gateway restartfrom an elevated shell if the gateway fails to restart after the update, restoring the task to a known-good state.WINDOWS_ACCESS_DENIED_PATTERNSinstructs adding the locale only when a real report surfaces it, matching AGENTS.md's "handle real production states" guidance and avoiding speculative expansion.Related to #111756