Repository navigation
Description
What happened
I was working with t3 in headless mode for weeks, connecting to this machine from my other laptop, tablet, phone. Today I decided to install desktop app on this machine. Once I did connect to the service I noticed the pop up saying the t3 server can be updated so I clicked update. All was fine and dandy but I noticed I can't open my files anymore. So I went to terminal to check the service status which said service is corrupted and can be fixed with
t3 service install. Once I installed the service all my sessions and projects disappeared.
Diagnosis
The user's long-lived service was installed with --base-dir /mnt/workspace/Projects, so the unit's T3CODE_HOME pointed there and all state (685 threads, 6 projects, 704MB statev2.sqlite) lived under that base dir. The default ~/.t3 held only leftovers from first-day experiments. The server's self-update (0.0.46-nightly.20261005.2689 → 0.1.2-nightly.20261022.3526) completed fine.
The data "disappeared" because of two compounding behaviors when a t3 CLI is run without --base-dir against a unit installed with one:
-
t3 service statusreports a healthy foreign-base-dir unit as needing repair. Inapps/server/src/cloud/bootService.ts,statuscompares the on-disk unit againstdetectedManager.render(plan)whereplanis rendered for the CLI's owninput.baseDir(default~/.t3). A unit installed for a different base dir can never match, socurrentis false and the CLI prints "needs an update or repair / Next: Runt3 service installto repair it."statuseven computesinstalledBaseDirfrom the unit, but neither uses it in thecurrentcomparison nor surfaces it informatServiceStatus. -
t3 service installoverwrites the unit without comparing base dirs, and its downgrade guard reads the wrong state file.installreadspreviousStateTextfromstatePathunder the CLI'sinput.baseDir(~/.t3/runtime/service-state.json, which said0.0.46-nightly.20261005.2702— equal to the CLI, so no refusal) instead of the unit's actual base dir (/mnt/workspace/Projects/runtime/service-state.json, which said0.1.2-nightly.20261022.3526). It then stops the running service and rewrites the unit for~/.t3. The real instance is silently replaced by a server bound to an empty state dir — to the user, total data loss.
Notably, restart and uninstall both already guard on this exact condition (bootServiceBaseDirOf(unit) vs path.resolve(input.baseDir)) and refuse to touch a foreign unit. install — the only destructive one of the three — has no such guard. Verified at tag v0.0.46-nightly.20261005.2702 (cfa4f76) and still present on main as of 2026-10-22.
Suggested direction: give install the same installedBaseDir comparison and refuse (or require an explicit flag) when it differs; read the downgrade-guard state from the unit's base dir; and print installedBaseDir in t3 service status so the mismatch is visible instead of being reported as corruption.
Steps to reproduce
t3 service install --base-dir=/some/other/dirand use the server (create a thread).- Install a second
t3CLI (e.g. a distro package) and runt3 service statuswith no flags → reports "needs an update or repair" although the service is healthy. - Run
t3 service installwith no flags, as the status output suggests. - The unit is rewritten for the default
~/.t3; the original instance stops and the service now serves an empty state dir. All sessions/projects appear gone. No downgrade or mismatch warning is shown even when the replaced service was newer than the CLI.
Version
Service that was clobbered: 0.1.2-nightly.20261022.3526 (self-updated from 0.0.46-nightly.20261005.2689). CLI that clobbered it: 0.0.46-nightly.20261005.2702 (AUR t3code-nightly-bin). Missing guard confirmed on main.
Environment
Linux x64 (CachyOS, kernel 7.2.9-2), Node v26.8.2, systemd user service.
Evidence
# Unit after `t3 service install` (no --base-dir) — rebound to default home:
Environment=T3CODE_HOME=~/.t3
ExecStart=~/.t3/runtime/versions/0.0.46-nightly.20261005.2702/t3 __service-launcher
# Real instance, cleanly shut down at the moment of the reinstall:
# /mnt/workspace/Projects/userdata/logs/boot-service.log
[21:22:41.924] INFO: Server update prepared; handing off to the service launcher.
[21:22:47.756] INFO: Listening on http://127.0.0.1:3773
[22:14:57.696] INFO: V2 orchestration shutdown reconciliation completed
# /mnt/workspace/Projects/runtime/service-state.json (ignored by the downgrade guard):
{ "protocol": 3, "activeVersion": "0.1.2-nightly.20261022.3526",
"update": { "fromVersion": "0.0.46-nightly.20261005.2689",
"targetVersion": "0.1.2-nightly.20261022.3526", "status": "committed" } }
# Replacement server booting on the stale default-home state:
# ~/.t3/userdata/logs/boot-service.log
[22:14:59.774] WARN: Database migration history diverges from this build; recorded
migration ids are skipped, not reconciled by name.
Related issues
#5749 (SSH reconnect can replace an external T3 service with a competing managed runtime) is the same family — one T3 install clobbering another's service ownership — but a different entry point and mechanism; this one is driven by the service status → service install repair suggestion ignoring installedBaseDir.
Fix applied or workaround
Recovered on the user's machine with no data loss: removed the manual systemd override, then ran the pinned runtime's own CLI — /mnt/workspace/Projects/runtime/versions/0.1.2-nightly.20261022.3526/t3 service install --base-dir=/mnt/workspace/Projects. Workaround until fixed: always pass --base-dir to t3 service ... commands when the service was installed with one.
Filed by
Claude Fable 5 (claude-fable-5) via t3 triage (Claude Code)
Activity
refactor(server): trim the base-dir fix to the install guard
fix(server): t3 service install no longer rebinds another home's service
refactor(server): trim the base-dir fix to the install guard
revert(server): drop the unknown-home install refusal