Skip to content

fix: retry transient macOS service upgrades - #81

Merged
wolfiesch merged 1 commit into
mainfrom
codex/launchctl-upgrade-retry
Jul 19, 2026
Merged

fix: retry transient macOS service upgrades#81
wolfiesch merged 1 commit into
mainfrom
codex/launchctl-upgrade-retry

Conversation

@wolfiesch

Copy link
Copy Markdown
Collaborator

Outcome

Mac app upgrades now survive launchd’s asynchronous service-removal window instead of restoring the previous backend or leaving the phone gateway stopped.

Changes

  • retry only launchctl bootstrap error 37 / Operation already in progress for up to three seconds
  • apply the same bounded behavior to the appserver service manager and Tailnet gateway installer
  • preserve normal failure behavior for every other command and error
  • cover the exact upgrade race and retry classification in tests

Verification

  • mise exec node@24.13.1 -- pnpm check
  • mise exec node@24.13.1 -- pnpm test (1,586 tests)
  • focused service-manager and Tailnet service tests
  • previously timed-out maintainer integration cases rerun alone: 25/25 passed
  • live macOS evidence: launchd logged bootstrap error 37 immediately after bootout; the identical operation succeeded after removal completed

Copilot AI review requested due to automatic review settings July 19, 2026 20:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens macOS service upgrades against launchd’s asynchronous service-removal window by adding a narrowly scoped, time-bounded retry for launchctl bootstrap failures classified as transient (“Operation already in progress” / exit code 37). This prevents upgrades from rolling back to prior backends or leaving the Tailnet phone gateway stopped during the brief replacement race.

Changes:

  • Add a retry loop (max ~3s, 100ms backoff) that only applies to launchctl bootstrap transient EINPROGRESS-style failures.
  • Apply the same transient classification + bounded retry behavior in both the Tailnet gateway installer script and the service-manager package.
  • Add targeted tests covering the retry classification and the specific upgrade race.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
scripts/tailnet-service.mjs Adds transient-error classification and bounded retry around launchctl bootstrap execution.
scripts/tailnet-service.test.mjs Adds unit test ensuring only the intended launchctl bootstrap error is retryable.
packages/service-manager/src/index.ts Adds bounded retry logic in runChecked for transient launchctl bootstrap failures.
packages/service-manager/test/service-manager.test.ts Adds lifecycle test validating MacLaunchAgentManager.install() retries bootstrap during removal window.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@wolfiesch
wolfiesch merged commit 3fc3d0b into main Jul 19, 2026
5 checks passed
@wolfiesch
wolfiesch deleted the codex/launchctl-upgrade-retry branch July 19, 2026 20:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants