← Back to PRs

#21591: fix(update): prevent double restart when refreshing service env

by irchelper open 2026-02-20 04:23 View on GitHub →
cli size: XS
## Problem `runDaemonInstall({ force: true })` calls `installLaunchAgent` which performs `launchctl bootout + bootstrap + kickstart -k`, restarting the service itself. The code introduced in #21071 then also ran `runRestartScript` (another `kickstart -k`) or `runDaemonRestart`, causing two competing restarts that each trigger startup hooks (e.g. `BOOT.md`). **Observed symptoms:** - 5 duplicate on-boot notifications after `openclaw update` - 3 duplicate on-boot notifications after `openclaw gateway restart` - `gateway.err.log` shows repeated `gateway already running (pid …); lock timeout after 5000ms` from launchctl racing with itself ## Root Cause In `maybeRestartService`, after `runDaemonInstall` succeeds (which internally does `kickstart -k`), the code continued to execute `runRestartScript` (which also does `kickstart -k` after a 1 s delay). This is a regression from 20004711d which removed the `serviceRefreshed` guard that originally prevented the double-restart. ## Fix Set `restartInitiated = true` after a successful `runDaemonInstall` so the subsequent `runRestartScript` / `runDaemonRestart` branches are skipped — the service has already been restarted by the install's `kickstart`. ``` runDaemonInstall (kickstart ✓) → skip runRestartScript → skip runDaemonRestart ``` The fallback paths still work: - If `runDaemonInstall` **throws**, `restartInitiated` stays `false` and the existing fallback runs (`runDaemonRestart` on failure, as tested). - If `refreshServiceEnv` is false, the restart-script / daemon-restart paths run as before. ## Testing - Updated two tests to match the corrected behavior. - All 44 update-cli tests pass. - `pnpm check` format: ✅ (TS2742 errors in other files are pre-existing, unrelated to this change) <!-- greptile_comment --> <h3>Greptile Summary</h3> Prevents duplicate service restarts during `openclaw update` by setting `restartInitiated = true` after successful `runDaemonInstall`. - Fixed regression from commit 20004711d which removed the `serviceRefreshed` guard - `runDaemonInstall` internally performs `launchctl kickstart -k` (confirmed in `src/daemon/launchd.ts:403`), making additional restarts redundant - Adds conditional guards (`!restartInitiated &&`) to skip `runRestartScript` and `runDaemonRestart` when install already restarted the service - Preserves fallback behavior: if `runDaemonInstall` throws, `restartInitiated` stays false and normal restart paths execute - Updated tests correctly verify the new behavior across all paths <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with high confidence - The fix correctly addresses a clear regression with a simple, well-scoped change. The logic is sound across all code paths, the root cause analysis is accurate (verified by examining git history and the `installLaunchAgent` implementation), and comprehensive test coverage validates both the fix and fallback behavior. No security concerns or edge cases identified. - No files require special attention <sub>Last reviewed commit: 0d162b9</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs