You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In-app self-update never rewrites ~/.t3/runtime/service-launcher.mjs, so every host running the background service is still using whichever launcher its last CLI-driven install wrote. #11510 changed how the launcher resolves a runtime's entry point, and #11607 changed what the installed runtime tree contains — together they make those persisted launchers reject every new release.
The compatibility gate for exactly this case already exists (runServicePreflight blocks on a launcherProtocol mismatch with "This release requires a newer T3 Code service launcher"), but SERVICE_LAUNCHER_PROTOCOL was left at 2 through #11510, so it never fires. The user gets a misleading error instead.
Let the server reach 0.0.41-nightly.20260914.1722 — the last release whose runtime archive still contained node_modules/t3/dist/bin.mjs.
From any client, run Update server to 0.0.41-nightly.20260915.1735 or newer.
Expected behavior
Either the update lands, or it is blocked up front with the actionable message that servicePreflight.ts already contains: "This release requires a newer T3 Code service launcher. Update it on the server machine."
Actual behavior
The update fails, identically and permanently, on every attempt:
The new runtime downloads and installs completely, sentinel and all.
The server executes the staged binary's preflight, which returns ready.
The launcher then rejects the handoff, and the app shows: Server update failed: The requested target runtime is missing or incomplete.
That message is wrong in a way that costs debugging time: the target runtime is complete and was just successfully executed by the same code path. What is actually stale is the launcher.
The mechanism:
The deployed launcher resolves entryPath as <versionDir>/node_modules/t3/dist/bin.mjs.
So runtimeExists() returns false and the launcher replies update-rejected (serviceLauncher.ts:509).
0.0.41-nightly.20260914.1722 shipped both layouts, which is why it was the last version to install successfully.
Impact
Major degradation or frequent failure
Silent and self-perpetuating: the update looks like it is working (the version installs fully), the error suggests a corrupt download, and nothing points at the real fix. It should hit any background-service host whose launcher predates #11510, which is effectively all of them, since in-app self-update never replaces that file.
Version or commit
Server stuck on 0.0.41-nightly.20260914.1722; failing to reach 0.0.41-nightly.20260915.1735 and .1766. Analysis against main @ 7235701.
Environment
Ubuntu 24.04 arm64, systemd user service, nightly channel, headless (no desktop app).
Logs or stack traces
# ~/.t3/userdata/logs/server.trace.ndjson, span cloud.server_self_update.update
ServerSelfUpdateError: Server update failed: The requested target runtime is missing or incomplete.
at failWith (.../versions/0.0.41-nightly.20260914.1722/t3:87571:97)
at cloud.server_self_update.update (.../versions/0.0.41-nightly.20260914.1722/t3:87533:40) {
[cause]: ServiceLauncherRejectedError: The requested target runtime is missing or incomplete.
}
# state after two failed attempts: new runtimes fully installed, never activated
$ ls ~/.t3/runtime/versions/
0.0.41-nightly.20260914.1722 <- active
0.0.41-nightly.20260915.1735 <- installed, valid .install-complete, never used
0.0.41-nightly.20260915.1766 <- installed, valid .install-complete, never used
$ cat ~/.t3/runtime/service-state.json
{"protocol":2,"activeVersion":"0.0.41-nightly.20260914.1722","update":{...,"status":"committed"}}
# untouched since the .1722 update; the launcher never records a pending update
$ ls -l ~/.t3/runtime/service-launcher.mjs
-rw-rw-r-- 1 ... 21382 Aug 10 17:39 # never rewritten by ~25 successful self-updates
$ grep -n 'entryPath'~/.t3/runtime/service-launcher.mjs
116: entryPath: NodePath.join(versionDir, "node_modules", "t3", "dist", "bin.mjs"),
Note: ~/.t3/userdata/logs/boot-service.log had been deleted on disk while systemd still held the fd, so the launcher-side log was only reachable via /proc/<launcher-pid>/fd/1. Possibly worth a separate look — nothing in the repo appears to unlink it.
Workaround
Run t3 update on the server machine from a current on-disk binary. This rewrites the unit to ExecStart=<versionDir>/t3 __service-launcher, which hosts the launcher inside the versioned executable and fixes it permanently:
Confirmed working — the service came back on .1766, with drop-ins and Tailscale Serve intact.
Suggested fix
Bump SERVICE_LAUNCHER_PROTOCOL to 3. The preflight then blocks before anything downloads and emits the correct message. Worth considering alongside it: any layout change to runtimePaths is a launcher compatibility break by definition, since that file outlives every self-update, so a comment on the constant tying the two together would help the next person.
Area
apps/server
Summary
In-app self-update never rewrites
~/.t3/runtime/service-launcher.mjs, so every host running the background service is still using whichever launcher its last CLI-driven install wrote. #11510 changed how the launcher resolves a runtime's entry point, and #11607 changed what the installed runtime tree contains — together they make those persisted launchers reject every new release.The compatibility gate for exactly this case already exists (
runServicePreflightblocks on alauncherProtocolmismatch with "This release requires a newer T3 Code service launcher"), butSERVICE_LAUNCHER_PROTOCOLwas left at2through #11510, so it never fires. The user gets a misleading error instead.Steps to reproduce
service-launcher.mjswas written 2026-08-10) and let it self-update through the app for a few weeks. The launcher file is never replaced.0.0.41-nightly.20260914.1722— the last release whose runtime archive still containednode_modules/t3/dist/bin.mjs.0.0.41-nightly.20260915.1735or newer.Expected behavior
Either the update lands, or it is blocked up front with the actionable message that
servicePreflight.tsalready contains: "This release requires a newer T3 Code service launcher. Update it on the server machine."Actual behavior
The update fails, identically and permanently, on every attempt:
ready.Server update failed: The requested target runtime is missing or incomplete.That message is wrong in a way that costs debugging time: the target runtime is complete and was just successfully executed by the same code path. What is actually stale is the launcher.
The mechanism:
entryPathas<versionDir>/node_modules/t3/dist/bin.mjs.serviceLauncher.tson main resolves<versionDir>/t3(changed in af6c138 / feat(server): manage runtimes as release archives only, never from npm #11510).t3package at all — on my box,0.0.41-nightly.20260915.1766/node_modules/has nine entries andt3is not one of them.runtimeExists()returns false and the launcher repliesupdate-rejected(serviceLauncher.ts:509).0.0.41-nightly.20260914.1722shipped both layouts, which is why it was the last version to install successfully.Impact
Major degradation or frequent failure
Silent and self-perpetuating: the update looks like it is working (the version installs fully), the error suggests a corrupt download, and nothing points at the real fix. It should hit any background-service host whose launcher predates #11510, which is effectively all of them, since in-app self-update never replaces that file.
Version or commit
Server stuck on
0.0.41-nightly.20260914.1722; failing to reach0.0.41-nightly.20260915.1735and.1766. Analysis againstmain@ 7235701.Environment
Ubuntu 24.04 arm64, systemd user service, nightly channel, headless (no desktop app).
Logs or stack traces
Note:
~/.t3/userdata/logs/boot-service.loghad been deleted on disk while systemd still held the fd, so the launcher-side log was only reachable via/proc/<launcher-pid>/fd/1. Possibly worth a separate look — nothing in the repo appears to unlink it.Workaround
Run
t3 updateon the server machine from a current on-disk binary. This rewrites the unit toExecStart=<versionDir>/t3 __service-launcher, which hosts the launcher inside the versioned executable and fixes it permanently:Confirmed working — the service came back on
.1766, with drop-ins and Tailscale Serve intact.Suggested fix
Bump
SERVICE_LAUNCHER_PROTOCOLto3. The preflight then blocks before anything downloads and emits the correct message. Worth considering alongside it: any layout change toruntimePathsis a launcher compatibility break by definition, since that file outlives every self-update, so a comment on the constant tying the two together would help the next person.