[Bug]: macOS `t3 service install` reports "already installed" while launchd still runs the previous launcher
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 23k
- Forks
- 5.9k
- Avg merge
- 11h 14m
- Merged PRs (30d)
- 357
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
macOS 15, background service already installed from an older release.
npx t3@nightly service install→ fails withBackground setup failed while starting the service (exit code 5)(that step is #11995: the old job survivesbootout --wait, sobootstrapgets EIO).npx t3@nightly service installagain.
Expected behavior
The retry repairs the service, or reports that it is not running the installed version.
Actual behavior
The retry prints T3 Code service is already installed with t3@0.0.43-nightly.20260917.1837. and exits 0 without touching launchd. Disk says the new version; launchd still runs the old one:
$ grep -A1 ProgramArguments ~/Library/LaunchAgents/com.t3tools.t3code.service.plist | tail -1
<string>/Users/me/.t3/runtime/versions/0.0.43-nightly.20260917.1837/t3</string>
$ launchctl print gui/501/com.t3tools.t3code.service | grep -A2 arguments
arguments = {
/Users/me/.nvm/versions/node/v22.22.1/bin/node
/Users/me/.t3/runtime/service-launcher.mjs
Every remote "Update server" then keeps failing with This release requires a newer T3 Code service launcher. Update it on the server machine. (#11940), and the command that message points to says everything is fine. Only a reboot or a manual launchctl bootout + bootstrap breaks the loop.
Cause
BootService.status on launchd compares only the unit file, runtime sentinel, and service-state.json against the CLI version (apps/server/src/cloud/bootService.ts, status). A failed bootstrap leaves no trace, so current is true and reconcileService short-circuits. systemd gets service-stopped from readSystemdProblems; launchd has no equivalent.
Suggested fix
Write the existing .restart-pending marker before the unit write on every install, not only for start: false. A successful activate already removes it, and the launcher removes it when it comes up on the awaited version. A failed bootstrap then leaves the marker, status reports restart-pending, and the retry re-runs stop/start. Together with the bootout fix for #11995 the retry actually replaces the old job. Two-line change; I can open a PR.
Related
- #11995 / PR #12005:
bootout --waitexits 64 on macOS 15, sobootstrapfails with EIO. That is step 1 above; this issue is the retry after it. - #11934, #12020, PR #11940: the protocol gate that blocks remote updates under a pre-#11510 launcher. Correct behavior, but it sends users to
service install, which then lies. - PR #7874: an earlier attempt at the same
bootoutwait, closed as believed fixed.
Impact
Major degradation or frequent failure
Version or commit
t3@0.0.43-nightly.20260917.1837, main 6d1d549441
Environment
macOS 15.7.4 (24G517), Apple Silicon, service installed with npx t3@nightly service install, remote updates from the desktop client
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in apps/server/src/cloud/bootService.ts, especially BootService.status and reconcileService, and trace how service-state.json and the .restart-pending marker affect installation retries. Reproduce the macOS launchd scenario from the issue, then verify that a failed bootstrap is detected and a subsequent service install retries reconciliation instead of reporting the old launcher as current.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos, node.js, typescript
- Domain
- devops, operating-systems
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 84/100