Uh oh!
There was an error while loading. Please reload this page.
Fix daemon reinstall race with a still-loaded LaunchAgent - #147
Conversation
launchctl bootout is asynchronous: launchd may still be tearing the service down after the command returns, so reinstalling over a live agent can bootstrap into a half-removed state and fail with "Bootstrap failed: 5: Input/output error". installLaunchAgent now polls `launchctl print` until launchd has released the label after bootout, and retries the transient EIO (error 5) on bootstrap a bounded number of times. Genuine load/config errors are not retried and surface immediately as before. The launchctl runner is already injectable; the test-only `sleep` seam keeps the regression test fast. Documents the decision in LLP 0017 and annotates the code. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
philcunliffe
commented
Jun 25, 2026
🤖 neutral dual-review — verdict: approve Risk: Low — isolated, well-tested change to a single macOS daemon-install helper ( Codex key points (gpt-5.5, ran): No findings. Fix is correct; regression test is non-vacuous — by inspection three of the four tests fail on Claude key points: Fix is correct and matches the issue/LLP 0017 intent: Test non-vacuousness — empirically verified: I ran the new test against the unpatched Findings:
No blocker or major issues from either reviewer → approve. Left as draft (ready-hold handled by next tick). |
Uh oh!
There was an error while loading. Please reload this page.
What
installLaunchAgent(macOS daemon installer) now closes the bootout→bootstrap race that madehyp daemon installfail when reinstalling over a still-loaded LaunchAgent.Why
launchctl bootoutis asynchronous — launchd may still be tearing the service down after the command returns. Bootstrapping into that half-removed state fails withBootstrap failed: 5: Input/output error. This is issue #128.How
launchctl printuntil launchd has released the label (bounded, ~3s ceiling) before writing the new plist and bootstrapping.error 5) on bootstrap a bounded number of times (3), re-waiting for release between attempts.LaunchAgentError, exactly as before.launchctlrunner was already injectable; added a test-onlysleepseam so the regression test runs without real waits.LLP 0017#reinstall-waits-for-launchd-releaseand annotated the installer with a matching@ref.Verified
test/core/daemon-launchagent-race.test.jsdrives a fake launchctl with scripted result queues. 3 of its 4 cases (wait-before-bootstrap, EIO-retry-succeeds, bounded-retry-then-throw) fail on master and pass with this fix; the 4th (genuine error fails fast) passes both before and after, guarding against over-eager retry.npm testgreen: 1386 pass, 1 skipped, 0 fail.@refannotations insrc/core/daemon/macos.jsresolve cleanly against the LLP corpus.Fixes#128