25 runtimes in 11 days, ~7.4 GB
[Bug]: On macOS, runtime prune never deletes a version whose directory was on the service PATH#25399
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
- On macOS, install the background service with
t3 service installon version A. The LaunchAgent'sPATHincludes~/.t3/runtime/versions/A. - Start the service. Its startup prune calls
shellLauncherTargets, which looks fort3in everyPATHdirectory, finds~/.t3/runtime/versions/A/t3, and appends that path to~/.t3/runtime/launchers. - More than an hour after A was installed, update to version B with
t3 update. - Restart the service and list
~/.t3/runtime/versions.
Expected behavior
A is deleted: it is not active, not running, and no t3 launcher points at it.
Actual behavior
A stays. ~/.t3/runtime/launchers lists ~/.t3/runtime/versions/A/t3, which resolves into A, so selectUnusedRuntimes counts A as referenced. That file is only ever appended to, so A stays pinned even after its directory leaves PATH. Every version whose directory reaches the service's PATH is kept for good, about 270 MB each.
Seen on two Macs going from 0.1.0 to 0.1.2. On one of them, the startup prune after the service was reinstalled on 0.1.2 logged Removed unused t3 runtimes. { versions: [ '0.1.1' ] }. It removed 0.1.1, which only ever ran through client-started server updates and never reached the plist, and kept 0.1.0, whose t3 was in runtime/launchers. Both Macs now have versions/0.1.2/t3 recorded, so 0.1.2 will be kept after the next update too.
Our Linux hosts are not affected: the systemd unit T3 writes does not set PATH, and their runtime/launchers holds only ~/.local/bin/t3.
Cause on main @ 1ecb1a0f9346c7902e98d4362cee0ececd93962c:
- bootService.ts#L547-L558: the LaunchAgent's
PATHincludespath.dirname(host.execPath), a runtime version directory. #17532 reports the same entry from another side: a plaint3in the service resolves to an old build. - pinnedRuntime.ts#L406-L409:
shellLauncherTargetschecks everyPATHdirectory for at3, so it finds the runtime's own entry binary. - pinnedRuntime.ts#L418-L445: any
t3whose real path is inside the versions directory is appended toruntime/launchers. Nothing removes entries. - pinnedRuntime.ts#L561 and #L575-L576: prune keeps any version a recorded launcher resolves into.
Possible fixes, for maintainers to choose: have shellLauncherTargets skip a t3 that is itself a runtime's entry binary (directly inside versions/<version>/), both on PATH and in the recorded file, which also unpins existing installs; and keep runtime directories off the service's PATH (#17532).
Impact
Minor bug or occasional failure
Version or commit
0.1.2; the code above is unchanged on main @ 1ecb1a0
Environment
macOS 26.6 and 26.2 (arm64), launchd user service
Logs or stack traces
| 1 | $ cat ~/.t3/runtime/launchers |
| 2 | ~/.local/bin/t3 |
| 3 | ~/.t3/runtime/versions/0.1.2/t3 |
| 4 | $ /usr/libexec/PlistBuddy -c "Print :EnvironmentVariables:PATH" ~/Library/LaunchAgents/com.t3tools.t3code.service.plist | tr : '\n' | grep runtime/versions |
| 5 | ~/.t3/runtime/versions/0.1.2 |
Workaround
After each update, delete the previous version's directory and its line in ~/.t3/runtime/launchers. Keep the version the LaunchAgent's ProgramArguments points at (#17490).