Repository navigation
Description
Follow-up to #5749, as requested in #5749 ("slow or restarting external services can still lose the 2-second probe and trigger a competing launch attempt... please open a new issue for that part only"). This issue covers only that part.
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 (service launcher), plus the SSH launch script in packages/ssh
Steps to reproduce
- Linux host runs the supported user service (
t3code.service→__service-launcher), linger on, T3 Connect linked. Default home~/.t3, port 3773. - A macOS desktop client has an SSH connection to the same host.
- Hit the restart gap on purpose:
systemctl --user stop t3code.service, then let the desktop reconnect.ssh-launch/<key>/managedflips tomanaged. systemctl --user start t3code.service. The child exits 78 and the launcher goes idle.- Quit the desktop. Nothing is listening anymore, and
t3code.servicestill shows active.
In normal use, step 3 is any service restart (an update, a reinstall, a unit change) that overlaps a desktop reconnect.
Expected behavior
The desktop waits for the restarting service and adopts it as external. If something else does win the race, the service takes ownership back once that server exits, without anyone stepping in.
Actual behavior
- The launch script finds no live runtime record, so it starts a managed server on the default home and writes
managed(packages/ssh/src/tunnel.ts, launch script). - The service's child then hits the fix(server): enforce one database owner and guard update rollback #16102 owner lock, exits 78, and the launcher calls
#idleWhileStateDirOwned(apps/server/src/serviceLauncher.ts). It stays active with no child, indefinitely. - Every agent now runs on the desktop-managed server. Even after fix(ssh): reconnecting keeps the remote server and its agents running #19432, that server is stopped on desktop quit and on explicit Disconnect/Remove. Keeping agents independent of the desktop is what the service setup is for.
- After the managed server exits, the host stays without a server until someone runs
systemctl --user restart t3code.service. The launcher never notices the lock was released.
Impact
The host silently stops being service-owned. Running agents then depend on the desktop's lifetime, and nothing shows that ownership changed. On this host it went unnoticed for 5 days.
Version or commit
Oct 18: launcher 0.0.46-nightly.20261015.3176 (unit ExecStart) supervising child 0.1.1-nightly.20261018.3331, with a Nightly desktop. Oct 23: the desktop's runner was 0.1.3-nightly.20261023.3579, built after #19432. With the service down, it started a managed server on the default home within seconds, twice. The idle path is unchanged on main at e340aae.
Environment
Fedora 44 host, systemd user service, linger on. macOS desktop over SSH. Phone over T3 Connect.
Logs or stack traces
The service was down from 18:51:35 to 18:53:02. The desktop logged in over SSH and its managed server came up inside that window:
Oct 18 18:51:33 sshd-session: Accepted publickey for <user> from <desktop tailnet IP>
Oct 18 18:51:35 systemd: Stopped t3code.service - T3 Code server.
ssh-launch/<key>/server.log [18:52:31.212] INFO (#337): Listening on http://127.0.0.1:3774
Oct 18 18:53:02 systemd: Started t3code.service - T3 Code server.
boot-service.log:
[18:53:03.417] ERROR (#1): ServerAlreadyRunningError: A T3 Code server already owns /home/<user>/.t3/userdata. Stop that server, or use a separate T3 home and pair with it, then retry. No server was stopped.
[service-launcher] Another T3 Code server owns this T3 home. Stop it, then restart this service.
Five days later: t3code.service was active with only __service-launcher in its cgroup, nothing was listening for the service, and ssh-launch/<key>/managed said managed. #5749 has a matching report from another user (lg-epitech, an update handoff with a 15 s DB backup gap). In that report the launcher also stayed idle after the stray server exited.
Proposed fix
Both current behaviors look intentional, so this keeps their reasons intact:
- fix(ssh): reconnecting keeps the remote server and its agents running #19432 kept the adoption fallback so a stale runtime record cannot block reconnects.
- fix(server): enforce one database owner and guard update rollback #16102 made the launcher idle on exit 78 so it does not fall into a systemd/launchd restart loop.
1. Launcher recovers on its own (host side, no protocol change). While idle in #idleWhileStateDirOwned, check the owner lock every ~30 s without holding it. One way: try a non-blocking exclusive transaction on userdata/server-owner.sqlite and release it at once. When the lock is free, start the active version's child again. Polling inside the launcher causes no systemd restarts, so the restart loop #16102 prevented stays prevented. A pending update is still marked failed as it is today. This change alone turns "dead until a manual restart" into "back within ~30 s of the stray server exiting".
2. Optional: the SSH launch script skips the race while a live supervisor is restarting. Fallback only when no live launcher exists. The launcher would publish a small liveness record (PID plus process start time, the same PID-reuse guard #16102 uses for server-runtime.json). The launch script would then work like this:
- No live runtime record, and that launcher PID is alive: wait for the runtime record within the existing cold-start budget (
REMOTE_READY_TIMEOUT_MS), then adopt it asexternal. - No launcher record, or a dead one: same as today, including the fallback launch.
A stale record still cannot block reconnects, because a dead launcher PID falls through to today's path. Fix 2 needs both host and client to update. Fix 1 alone covers the failure.
I can open a PR for fix 1 if this direction works for you.
Workaround
Stop the desktop-managed server and wait for its PID to exit, because the lock can outlive the port. Set ssh-launch/<key>/managed to external and remove pid. Then systemctl --user restart t3code.service.
Triaged with Claude Code (claude-opus-5-5).
Activity
ishanavasthi commentedon Oct 25, 2026
Another data point for this issue: the same race also happens when the service updates itself, with no desktop-app update and no manual
systemctl stop.Setup: Ubuntu 26.04 host,
t3code.serviceuser unit, default home~/.t3, port 3773. A macOS desktop is connected over SSH (Tailscale). The unit'sExecStartstill pointed at the0.0.46-nightly.20261007.2761launcher, which supervised child0.1.2-nightly.20261021.3457.What happened (Oct 24):
Server update prepared; handing off to the service launcher.(target0.1.3-nightly.20261024.3630) and shut down.t3 serve --host 127.0.0.1 --port 3773 --base-dir ~/.t3, and wrotemanaged. That server took the owner lock.0.1.3candidate exited withServerAlreadyRunningError(78). The update was recorded asrolled-back, reason: candidate-exited:78, and the rollback to0.1.2exited 78 as well.Difference from the report above: the old
0.0.46launcher has no idle-on-78 path (#16102). It exited 1 each time (Active child exited unexpectedly (78)) and systemd retried every 5 s (RestartSec=5). After 6 attempts the unit hitStartLimitBurstand went tofailed. A current launcher would idle instead, which is the case #24225 describes, so both outcomes need #24230's recovery. Thefailedstate is at least visible, and ourfailed user unitshealth check alerted. An idle launcher shows asactiveand stays silent.Why #23005 doesn't cover this: the
desktop-update-in-progressmarker is written by the desktop updater. A headless service updating itself never writes it, so the SSH launch script has no signal to wait on. Fix 2 in this issue (the launcher publishing a liveness record that the SSH script checks) would cover this trigger as well.Workaround that worked: SIGTERM the SSH-launched
t3 serveand wait for it to exit, thensystemctl --user reset-failed t3code, thent3 update --yes 0.1.3-nightly.20261024.3630. That switchedactiveVersionto0.1.3and restarted the service. The service owned 3773 again andserver-runtime.jsonshowedserviceManaged: true. Starting the unit with plainsystemctl startwould have launched the rolled-back0.1.2against a database that0.1.3may already have migrated, so I used the update command instead.beastyrabbit commentedon Oct 29, 2026
Note
🤖 Claude Opus 5.5 responding on behalf of beastyrabbit
Another data point, this time on stable v0.1.5. It also shows a consequence I don't see covered here or in #24230: after the race, the service comes back on the old version against a database the new version already migrated.
Setup. Linux VM,
t3code.serviceuser unit (ExecStart→runtime/versions/0.1.3/t3 __service-launcher) with a drop-in settingT3CODE_HOST=0.0.0.0. Default home~/.t3, port 3773. A Linux desktop (AppImage, 0.1.5) on another machine had an SSH route and an HTTPS route to this host.What happened (Oct 29).
Server update prepared; handing off to the service launcher.). The desktop updated and restarted at the same moment.ServerAlreadyRunningError(78). From then on the home was owned byt3 serve --host 127.0.0.1 --port 3773 --base-dir ~/.t3, started by the desktop's SSH launch script from its own 0.1.5 runtime.activeVersion: "0.1.3"withupdate: { status: "failed", reason: "state-dir-owned" }.0.0.0.0bind was gone, so LAN clients couldn't reach it, and the unit still showedactive.The same evening it happened again, with cleaner timestamps. After I stopped the stray server and restarted the unit, the update ran again:
handing offat 20:43:01, the SSH launch script started a loopback server at 20:43:27, the candidate exited 78 at 20:43:30, andservice-state.jsonagain saidfailed / state-dir-owned. While a desktop keeps an SSH route to the host, the update never lands, because the restart it needs always opens the gap.The downgrade. The stray server runs the desktop's version, which here was the update target, and it migrated the database. The launcher then put
activeVersionback tofromVersion. When I restarted the unit at 20:34, it started 0.1.3 on that database:As I read #24230, its recovery would do this on its own: once the stray server exits, it "starts the active version again", which after a
state-dir-ownedfailure is the old build. A manualsystemctl --user restartdoes the same. The earlier comment here avoided it by runningt3 update --yes <target>instead of starting the unit. It might be safer if the recovery retried the failed update, or refused to start a build that doesn't know the database's latest migrations.Workaround we're using. We removed the SSH route from the desktop for every host that runs the service, and reach those hosts over their HTTP routes only. With no SSH route, nothing can start a competing server during the restart gap.
ViperJuice commentedon Oct 31, 2026
Additional matching observations on Linux hosts with Windows desktop SSH connections, October 30–31 nightly updates:
We have not established the exact race timing or reproduced the post-client-quit outage in this incident, and have not deleted ownership locks or stopped live servers. These observations support the ownership-handoff/status mismatch described here; they are not a claim that every causal step is independently proven. Local bootstrap/recovery tracking references this upstream issue rather than duplicating it.
jrgong420 commentedon Oct 31, 2026
Another occurrence on stable 0.1.5, from a service-initiated update (0.1.3 → 0.1.5) with a macOS desktop connected over SSH. This one adds a confusing message in the client UI.
Setup: Pop!_OS (Linux x64, kernel 6.12),
t3code.servicesystemd user unit (ExecStart→runtime/versions/0.1.5/t3 __service-launcher), drop-inT3CODE_HOST=127.0.0.1, default home~/.t3, port 3773, T3 Connect managed tunnel. Client: T3 Code desktop on a MacBook Pro, connected over SSH via Tailscale.Timeline (host-local):
About 17 s passed between the handoff and the SSH launch, and about 37 s before the new child tried to take ownership, so the gap is wide enough for a reconnect to land in it every time.
What the user saw:
t3 service installon the server machine." The service was installed and active. That message sent the user toward a reinstall that would not have helped, because the real cause was the SSH-launched server holding the lock.t3code.servicestayedactivewith only__service-launcherin its cgroup for about an hour. A manualsystemctl --user restart t3code.serviceat 10:15 failed the same way, because the stray server was still alive.Recurrence: the same handoff →
ServerAlreadyRunningErrorpair appears at least 4 more times in this host'sboot-service.logover recent updates. The matchingListening onlines inssh-launch/<key>/server.logsit right after each handoff.Workaround that worked: stop the SSH-launched server, then
systemctl --user restart t3code.service. The service came up on 0.1.5, the managed tunnel recovered (startManagedCloudTunnelIfOriginConfirmed→ Success), and the Mac reconnected. At its next reconnect the SSH launch script lost the race to the service (ServerAlreadyRunningErrorinssh-launch/<key>/server.logat 10:18:10) and adopted it asexternal.+1 for fix 1 (the launcher polling the owner lock while idle). Separately, the client's "Remote updates require the background service" message should tell apart "no service installed" and "service installed but locked out by another server".
Triaged with Claude Code (claude-opus-5-5) via
t3 triage.ViperJuice commentedon Oct 31, 2026
Reproduced again on nightly, this time from a client-initiated update with the service template and host memory protection already correct. This bears directly on #25633.
Setup: Linux host,
t3code.serviceuser unit (launcher and child on0.1.6-nightly.20261031.3956), default home~/.t3, port 3773,T3CODE_HOST=0.0.0.0. A Windows desktop (nightly) is connected to it over SSH via Tailscale. A second host showed the same failure in the same session.Timeline (host-local, Oct 31):
The update was recorded as
failed,state-dir-owned. The desktop showed the update as stuck.Observations:
t3 update --yeson a similar host with no client connected passed earlier the same day. That fits the race needing an SSH reconnect and is not evidence of a fix.On our side we treat #25633 (keep external ownership in the SSH launch path) as the prevention and #25237 as recovery. The only supported workarounds we can see are to close every client, or remove its SSH route, during updates, or to connect over a non-SSH route. If there is a supported way to keep the desktop from launching a server over SSH for an environment that is already service-managed, it would be good to document it here.
ViperJuice commentedon Oct 31, 2026
Another recurrence, this time with SSH ownership already recorded as
external. This is the case #25633 addresses.Setup: Ubuntu 26.04 host.
t3code.serviceuser unit on0.1.6-nightly.20261031.3956, default home~/.t3, port 3773. A Windows desktop is connected over SSH, and its~/.t3/ssh-launch/<key>/managedreadexternalbefore the update. The desktop's server update targeted0.1.6-nightly.20261031.3977.Timeline (UTC, 2026-10-31, from the host logs):
boot-service.log:Server update prepared; handing off to the service launcher.ssh-launch/<key>/server.log:Listening on http://127.0.0.1:3773. The desktop's launch script had started its ownt3 serveabout 17 s after the handoff.boot-service.log: the.3977candidate exits withServerAlreadyRunningError. The launcher idles, andservice-state.jsonrecordsupdate: {status: failed, reason: state-dir-owned, from .3956, target .3977}.ServerAlreadyRunningError.The same sequence occurred earlier on this host (00:00:20 → 00:03:05 EDT). A second Linux host with a Windows SSH client failed the same way on the same update. Both hosts ended up back on the service-owned
.3956with the update recorded as failed.Source check:
v0.1.6-nightly.20261031.3977does not changeserviceLauncher.ts,packages/sshorselfUpdate.tscompared with.3956.#idleWhileStateDirOwnedand the launch script's fallback (clearexternalafter a missed readiness probe, thenpick_port+t3 serve) are both unchanged.Because
externalwas already recorded here, #25633 (keep recorded external ownership through the readiness budget, and never launch a replacement) is the change that would have prevented this occurrence. #27275 would have ended the client's 32-minute "Restarting…" wait. Neither changes launcher recovery after a lost race, so a host stays idle until someone steps in.Maintainers: could you say whether #25633 is acceptable as the SSH-side fix, and whether launcher recovery (fix 1 here) is still wanted after #24230 was closed? Our workaround for now is to update with every SSH desktop client fully quit. We have not removed locks, killed servers or edited service state.
firstclassformula commentedon Nov 5, 2026
Recurrence on stable 0.1.7, on the same kind of setup as the Oct 31 report above (Pop!_OS host,
t3code.service, macOS desktop 0.1.7 over SSH via Tailscale, T3 Connect linked, default home~/.t3, port 3773). This adds three things: both attempts to reach 0.1.7 collided, the two servers then collided in the other direction, and the desktop's error pointed at the network.Timeline (host-local, CET):
What's new:
0.1.6runtime, so the first update to 0.1.7 does not appear to have stuck. This matches the version rollback described above on 0.1.5.t3 connect statuspassed on the host:The timing of that diagnosis is not fully explained: four attempts should take longer than the 80-second gap if the environment has two routes, and no failed attempts appear in the host trace. I can't confirm it came from this collision.
0.1.7 is the latest stable, #24230 was closed without merging, and #25633 is still open, so stable users have no fix yet.
Triaged with Claude Code (claude-opus-5-5) via
t3 triage.codewarnab commentedon Nov 6, 2026
Another reproduction with a Windows desktop connected over SSH to a Linux ARM64 T3 background service, both on Nightly.
During a server update, the managed server stopped for the handoff. An SSH-launched server appeared during that gap, and the update failed with
state-dir-owned. The service launcher remained active without its server child, and the desktop showed “restarting”. The manual-update warning returned afterward.Process and update-state snapshots support an ownership conflict during the handoff; they do not establish the exact internal reconnect decision. Please include Windows-to-Linux SSH connections when testing a fix.
abassaf commentedon Nov 6, 2026
Same idle-launcher state on macOS (launchd), triggered by a self-update handoff, which surfaces as "needs a manual update" in the update dialog
Adding a second environment. I haven't confirmed the cause of the competing server here. The effects match this issue.
Environment
com.t3tools.t3code.service→t3 __service-launcher), default home~/.t3, port 3773.0.1.8-nightly.20261105.4218. The client that showed the symptom was desktop nightly0.1.8-nightly.20261106.4247on Windows, connected remotely.What I saw
psshowedt3 serve --host 127.0.0.1 --port 3773 --base-dir ~/.t3with parent pid 1, not the__service-launcher. The launcher was running, but its state wasspeculativeand it had no child.boot-service.logshowed the sequence below. The sameServerAlreadyRunningErrorpair appears at three earlier times in the same log:Fix that worked
kill -TERM <stray server pid>, wait for exit, thenlaunchctl kickstart -k gui/<uid>/com.t3tools.t3code.service. The launcher then ran its own server child, and the user could update from the remote UI.Takeaways
Triaged with
t3 triage(CLI 0.1.7) using Claude Sonnet 5.5 in Claude Code.vpsman commentedon Nov 7, 2026
Note
🤖 Claude Opus 5.5 responding on behalf of vpsman
Still reproduces on 0.1.8-nightly.20261107.4314, and
tunnel.tsis unchanged on main at e455200. One trigger is left that neither open PR covers: an installed service that is stopped.Setup: a Linux host running
t3 service(systemd user unit, linger on) and two desktop clients connected over SSH. I rendered the launch and stop scripts from the tag and ran them over SSH the way a desktop does. They used a separate state key seeded withmanaged=externalandport=3773, like the desktops' record, so the desktops' own record stayed untouched.{"remotePort":3773,"serverKind":"external"}in 1.5 s.systemctl --user stop t3code.serviceat 17:47:57.39, launch started straight away. At 17:48:02.41 it printed{"remotePort":3773,"serverKind":"managed"}.server-runtime.jsonnamed a newt3 serve --host 127.0.0.1 --port 3773 --base-dir ~/.t3with parent PID 1 and noserviceManaged.systemctl --user start t3code.serviceat 17:48:09.69. Eight seconds later the unit readactive (running)withNRestarts=0and no child.boot-service.loghadServerAlreadyRunningErrorandAnother T3 Code server owns this T3 home.On the other host, during a client-requested update, the old server handed off at 16:53:15.35 and the new one listened at 16:53:19.17. That's a gap of about 4 s, twice the 2 s reuse probe. No desktop connected in that window, but after an unexpected stop the desktops re-ran the launch within 2 to 3 s.
What the open PRs change, from reading their diffs. I haven't run them.
external. That covers the ~4 s update swap. When no runtime is alive it still clears the record and starts a managed server, so step 2 still happens, 60 s later. Its description says it does not detect an installed service.A proposal for the remaining case: before falling back to
t3 serve, the launch checks whether a service is installed for the default home. If it is installed and not running, the launch starts it. Either way it then waits up toREMOTE_READY_TIMEOUT_MSfor the service's server and attaches asexternal. If that server never answers, as with the idle launcher in this issue, the launch fails instead of starting a second server. It never restarts a running service, because a restart mid-update would kill the trial child. Archive-mode hosts have no Node, so the check would be a new__ssh-helpersubcommand besidepick-portthat reusesBootService.status(installed, base dir) and the manager's start step. The dev runner (T3_NODE_SCRIPT_PATH) would skip it.This needs no new liveness record, unlike fix 2 above, because the service manager already knows whether the unit is installed. Would you take it as a follow-up to #25633, or should it get its own issue? I can implement it either way.
keskinonur commentedon Nov 8, 2026
Still reproduces on today's nightly (launcher
0.1.8-nightly.20261106.4275, update target0.1.8-nightly.20261108.4367). It's the same handoff race, with two details I haven't seen in this thread: a different desktop started the update, and while the stray server ran, no client without SSH could reach the host.Setup: Debian 12 VM,
t3code.serviceuser unit with linger, default home~/.t3, andT3CODE_HOST=0.0.0.0,T3CODE_PORT=3774inservice.env. Two macOS nightly desktops. Desktop A had an SSH environment for this host. Desktop B, a phone and peer links from other hosts reached it over HTTPS, through a reverse proxy to<host>:3774.Timeline (host-local):
Desktop B asked for the update over HTTPS, and desktop A, which only had an SSH route, happened to reconnect inside the handoff gap. The SSH desktop doesn't have to update or restart anything to trigger this.
The SSH-launched server took the service's port (3774) but listened on
127.0.0.1only, while the service binds0.0.0.0. From 16:44 to 17:09 the reverse proxy returned 502 for desktop B, the phone and the peer links. Desktop A saw a healthy host, and nothing on the host or in B's UI said the service no longer owned it.After the stray server exited, the launcher stayed idle until I restarted the service by hand. #25237 covers that part and would have ended the outage around 17:05. #25633 would probably have prevented the takeover at 16:44. Neither is in 4367. Until they land, the workaround I'm using for a host that runs the background service is to add it to desktops by its HTTPS address, never as an SSH environment.
ViperJuice commentedon Nov 11, 2026
Another variant of this race, hit during the launcher's update handoff rather than a plain restart, with an extra risk.
Environment: Linux host running
t3code.service, Windows desktop clients connected over SSH, nightlies 4449 → 4452 / 4465 / 4478 / 4491.What happens:
server.updateServerWithProgress).cpbackup of a 55 GBstatev2.sqlite(16–25 min here).t3 serve --host 127.0.0.1 --port 3773 --base-dir ~/.t3outside the service. It takesserver-owner.sqliteand serves, writing to the database while the launcher is still copying it, so the backup is torn.ServerAlreadyRunningError). The launcher recordsfailed / state-dir-owned, deletes its backup and idles with "Another T3 Code server owns this T3 home."We saw this four times (Nov 9 22:45 and 23:28; Nov 10 10:49 and again that evening), and once more during a maintenance window with the service stopped.
Extra risk: if the trial exits with any code other than 78,
#returnToPreviousrestores the torn copy over a database that the stray server is actively writing.Fix suggestion:
olderBootServiceHomerefuses an external server while the boot service is active andruntime/.restart-pendingexists, andt3 updatesets that file. But the launcher's own update handoff never does. Have the launcher set it, or hold the ownership lock, from handoff until the trial child starts.Workaround: run
t3 update <version> --yesfrom a shell outside T3. It sets.restart-pendingitself, does no copy, and has been reliable for us (4449 → 4478, 4491 → 4501).firstclassformula commentedon Nov 12, 2026
Still reproduces on stable 0.1.9 (service-initiated update 0.1.7 → 0.1.9, triggered from a macOS desktop 0.1.9 that also had an SSH environment for the host). Hit twice in a row, Nov 11 and Nov 12.
Setup: Pop!_OS (Linux x64, kernel 6.12),
t3code.serviceuser unit (ExecStart→runtime/versions/0.1.7/t3 __service-launcher), drop-inT3CODE_HOST=127.0.0.1, default home~/.t3, port 3773, T3 Connect linked. Client: T3 Code desktop 0.1.9 on a MacBook Pro, connected over SSH via Tailscale.Timeline (host-local, Nov 12):
The desktop shows "restarting…" indefinitely.
Two additions:
service-state.jsonstill saysactiveVersion: 0.1.7, so a plainsystemctl --user restartwould bring 0.1.7 back on a 0.1.9-migrated database (the same risk noted above for 0.1.5).t3 service install(the recommended repair) re-triggers the race itself: its stop/start opened another gap, and the still-connected desktop launched a second stray server within ~2 s (06:11:47 pid file → 06:11:51 ServerAlreadyRunningError). Recovery only worked once the desktop was fully quit.Workaround that worked: fully quit the SSH-connected desktop, stop the stray
t3 serve, runt3 service installfrom the 0.1.9 runtime (moves ExecStart + activeVersion to 0.1.9), then use T3 Connect instead of an SSH environment for that host.Posted by Claude Code (Opus 5.5) on behalf of the reporter, from a
t3 triagesession.abassaf commentedon Nov 13, 2026
Another repro: the stuck update is also visible from a remote client (Windows GUI → macOS service)
What happened: I pressed Update in the Windows desktop app, targeting a macOS service-managed server. At 15:38:11 the server logged
Server update prepared; handing off to the service launcher(target0.1.10-nightly.20261113.4616). About 14 seconds later an unmanagedt3 serve --host 127.0.0.1 --port 3773 --base-dir ~/.t3(older0.1.9-nightly.20261110.4452, parent pid 1) took the owner lock. The 0.1.10 server then failed withServerAlreadyRunningError.runtime/service-state.jsonrecordedupdate.status: "failed",reason: "state-dir-owned". The launcher printedAnother T3 Code server owns this T3 home. Stop it, then restart this service.and stayed idle.Client symptom: the Windows GUI stayed on "Updating" for 15+ minutes. It was connected to the orphan server, which never reported the failure.
Recovery: stopping the orphan was not enough. The launcher did not retry after the lock was released. I also had to run
launchctl kickstart -k gui/<uid>/com.t3tools.t3code.service.Environment: macOS arm64 service. The launchd unit still points at the
0.1.8-nightly.20261104.4165launcher (see #17490). The state DB is about 13 GB. The Windows client was the latest nightly.Unknown: what started the competing
t3 serve. I don't know whether the update flow itself or something else on this machine did.Triage CLI 0.1.9, with the diagnosis done by Claude (Sonnet 5.5) in a Claude Code agent session.
linksmith commentedon Nov 13, 2026
Another occurrence on Linux (x64, kernel 6.8), on builds older than the fix in #25237.
Setup:
t3code.servicesystemd user unit,ExecStart→runtime/versions/0.1.7-nightly.20261101.4048/t3 __service-launcher, default home~/.t3, port 3773, T3 Connect linked.What happened:
ServerAlreadyRunningError/ "Another T3 Code server owns this T3 home. Stop it, then restart this service." It then stayed idle for 11 days withNRestarts=0and no server child.0.1.10-nightly.20261112.4564/t3 serve --host 127.0.0.1 --port 3773process (parent pid 1) ran in an sshd login session scope (session-*.scope), not in the service cgroup. It was started on 12 Nov 09:51 CET from an SSH connection, most likely the desktop app's SSH environment.t3 service status(4564) reported "needs an update or repair … Runt3 service installto repair it."npx [email protected]then failed with "A T3 Code server is already running … Connect to that server, stop it before starting another".After repair on 0.1.10-nightly.20261113.4630:
0.1.10-nightly.20261113.4630/t3 service installrepointed the unit'sExecStartto the 4630 runtime and restarted the service. The launcher started a 4630t3 servechild inside the service cgroup, ran the pending DB migrations, and the server answered HTTP 200 on port 3773 within seconds (NRestarts=0). So the repair path works. I haven't yet seen whether 4630 recovers on its own if an SSH-started server takes the lock again.Triaged with Claude Code (Opus 5.5) via
t3 triage.