Found 2026-08-20 during the hydrabody v2.0.67 rollout (#537). Applies to every Windows scheduled-task service that uses pkg/updater (hydrabody, hydranode, and friends). Full write-up: hydrarelease docs/runbooks/runbook-updater-library.md, section "Service didn't restart (Windows scheduled task)".
restart_windows.go assumes the updater runs inside the service process: it calls os.Exit(0) and relies on the task's 1-minute recurring TimeTrigger to revive the task with the new binary. The runbook's documented remote deploy path runs " update --force" as a SEPARATE process via hydracluster exec or SSH. There, os.Exit(0) ends the CLI, not the service. The preceding "schtasks /Run" is a no-op because MultipleInstancesPolicy=IgnoreNew ignores it while the task instance runs. Result: binary replaced on disk, service keeps running the old image, and " version" via exec lies about what is running (it reports the on-disk binary).
The CLI's update renames the running service's image to .backup. Windows permits renaming a running image but not deleting it. From then on the running old service's PerformUpdate always fails: os.Remove(backupPath) fails silently (error ignored, updater.go ~line 199), and os.Rename(installPath, backupPath) returns "Access is denied". With the cluster signaling the new version each status tick, the log loops once per minute:
body update available: <version>
[updater] triggered check: backing up current version: rename ... Access is denied.
The service never restarts on its own. Observed simultaneously on cosmic-pretzel-98, boom-pickle-38, and chunky-turnip-23; recovered by bouncing the HydraBody task (schtasks /End then /Run) on each.
After any "update --force" via exec on a Windows node, bounce the task:
schtasks /End /TN <TaskName>; Start-Sleep -Seconds 3; schtasks /Run /TN <TaskName>
Fixed in hydrarelease pkg/updater v1.20.0 (commit 89126d2):
Shipped to the fleet in hydrabody v2.0.68 (dependency bump) and validated end to end with hydrabody v2.0.69, canary on chunky-turnip first:
Other services (hydranode v1.17.5, hydraguard, etc.) still ship older updaters and keep the old behavior until they bump hydrarelease; the runbook documents the manual task bounce for them.