Repository navigation
[Bug]: t3 <word> starts a second server on the background service's database, and the service can't delete what it creates #14629
Description
Activity
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.needs-triageIssue needs maintainer review and initial categorization.Issue needs maintainer review and initial categorization.
on Oct 1, 2026 Note
Grok responding on behalf of Julius.
Triage
Thanks for the thorough trace, @jacobglanz. The event sequence numbers and port numbers made this much easier to pin down. I confirmed it on current
main(5cc99e1, 0.0.44). Two separate defects are at work here. The rows that stay visible but can't be deleted are #14353 (open issue). What's new is that an unknown word aftert3starts a second server against the service's T3 home.Why
t3 <word>starts a servert3is the server command, and it takes an optional positional<cwd>argument (apps/server/src/bin.ts,sharedServerCommandFlagsinapps/server/src/cli/config.ts). The parser only reports an unknown subcommand when the command has subcommands and no positional arguments. Because<cwd>exists, words likeaccount,login,clients, andhelpare read as a working directory. Help comes from the--help/-hflags, sot3 helpdoesn't print it. The nearest real commands aret3 connect loginandt3 auth.resolveServerConfigcreates that directory. Web mode, the default, then turns onautoBootstrapProjectFromCwd, so startup adds a project named after the directory with aNew threadin it (apps/server/src/serverRuntimeStartup.ts). When port 3773 is taken,findAvailablePortbinds an ephemeral port (packages/shared/src/Net.ts), which explains 43421 and 45777 in your trace. The second process also overwritesserver-runtime.jsonwith its own pid. On a clean exit it deletes that file without checking whose pid is in it (clearPersistedServerRuntimeStateinapps/server/src/server.ts), so the service's discovery record is gone until the service restarts.Why the service can't delete those rows
The service loads its command model once at startup (
OrchestrationEngine).thread.deleteandproject.deletecheck that in-memory model (apps/server/src/orchestration/commandInvariants.ts), but the UI reads the projection tables, so the rows still appear. After a rejection, catch-up reads events after the in-memorysnapshotSequence. Your service events at 443–447 moved that cursor past the creates at 436–442, so those entities never made it into the model. This is the same gap described on #14353, and a restart clears it because startup reads the projections again. Thethread.pull-request.syncrejections come from the second process running its own reactors against the same database.Related work
#6097 (open issue) and #10360 (open PR) cover the desktop port scan. #7504 (open issue) is
t3 projecttreating a failed probe as no server. #14115 (open issue) is any second server on one base directory, and #9652 (open PR) adds a directory lock for it. That lock would stop the second engine and the stuck threads, butt3 accountwould still create./accountand wouldn't be reported as an unknown command, so the CLI parse needs its own fix.Until then, your workaround is right: restart the service, delete the stray directories by hand, and avoid running bare
t3or an unknown word on a machine where the service already owns the T3 home.t3 --helpprints help.- addedvia-triageFiled through npx t3 triageFiled through npx t3 triageand removedneeds-triageIssue needs maintainer review and initial categorization.Issue needs maintainer review and initial categorization.
on Oct 1, 2026 Note
Grok responding on behalf of Julius.
The main part of this is fixed by #15795, which just merged:
t3 <word>now rejects unknown bare words unless they name an existing directory,t3 helpprints help, and plaint3/t3 startcheck the recorded live server pid before creating directories or opening state, so it no longer starts a second server on the service's home. The rows that show up but can't be deleted are tracked in #14353. Closing this one; reopen if you still hit the second-server launch on a build that includes #15795.
Before submitting
Area
apps/desktop
Steps to reproduce
curl -fsSL https://t3.codes/install.sh | sh,t3 service install,t3 connect.~, runt3 account(or baret3). Ctrl-C it after it starts.Expected behavior
t3 helpprints help.t3 accountsays "unknown command" instead of treatingaccountas a directory.t3while the service owns~/.t3doesn't start a second server on the same DB (or refuses, or hands off to the running one).Actual behavior
With the background service running, typing
t3,t3 account,t3 login,t3 clientsort3 helpin a shell starts a full second server against the same~/.t3. Each word is taken as<cwd>, so it also creates an empty dir, a project and a "New thread".The service never loads those projects/threads into its command model. They show in the UI but can't be deleted, archived or settled. They stayed stuck for about 19 hours, until the service was restarted.
(Closely related: #14353, #6097, #7504. This is a new trigger for the same split-brain - the
t3CLI itself, not the desktop app - plus a data point for the #14353 recovery gap.)~/account,~/login,~/clients,~/helpget created, plus a project and empty thread for each (and a project for~from baret3).project.created/thread.createdevents existed,projection_projects/projection_threadsrows haddeleted_atnull, all projectors were at the latest sequence.server-runtime.jsonwas overwritten by the last short-lived server (its pid and port, both dead now). The service's own entry is gone.Suspected cause
Two parts.
The CLI default command is
start, and the positional<cwd>accepts any word. Sot3 help/t3 accountstart a full server with auto-bootstrap for./help,./account. Nothing checks that the service already owns this T3 home (same root problem as [Bug]: Desktop starts a second backend against the background service database #6097, but from the CLI, not desktop).The service then never sees those entities. This matches the [Bug]: Saved thread is reported missing and cannot be deleted, archived, or continued after two servers share its database #14353 triage exactly: the command read model is loaded once at startup, and the catch-up after a rejected command only replays events after the in-memory
snapshotSequence. The service appended its own events at 443-447, which movedsnapshotSequencepast 436-442, so those creation events are skipped forever. 448-451 had no service event after them, so the catch-up found them and settle worked.Side effect worth noting: the second server ran its own reactors (PR sync wrote seq 438 for a thread the service owned), and around then the service hit repeated
thread.pull-request.syncinvariant failures ("changed before pull request discovery"). Likely the two PR reactors racing.Impact
Minor bug or occasional failure
Version or commit
0.0.44 (service + CLI). Desktop client 0.0.42.
Environment
Server: Ubuntu 26.04 ARM64, headless,
t3 service install(t3 __service-launcher+t3 serve, default~/.t3). Client: Windows 11 desktop app via T3 Connect. Provider: Claude.Logs or stack traces
Screenshots, recordings, or supporting files
image.png
image 2.png
Workaround
Restarting the service fixed it. Deletes for these ids kept failing right up to the restart (last failure 16:39 UTC). After the new
t3 servecame up at 16:43,thread.deleteandproject.deletesucceeded for all of them (16:47-16:48), anddeleted_atis now set in the projection tables. Matches #14353: a restart rebuilds the command model.Then delete the stray
~/accountetc. dirs by hand. Avoid running baret3on a box that has the service installed.