[Bug]: macOS `t3 service install` reports "already installed" while launchd still runs the previous launcher

Open Beginner friendly
#12,197 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
84/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
macos, node.js, typescript

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.

Description

accepted bug via-triage
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.

  1. npx t3@nightly service install → fails with Background setup failed while starting the service (exit code 5) (that step is #11995: the old job survives bootout --wait, so bootstrap gets EIO).
  2. npx t3@nightly service install again.
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 --wait exits 64 on macOS 15, so bootstrap fails 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 bootout wait, 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

Dominant language
TypeScript
Stars
23.2k
Forks
5.9k
Avg merge
10h 57m
Merged PRs (30d)
365

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from pingdotgg/t3code

All issues in pingdotgg/t3code

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.