Skip to content

[Bug]: t3 <word> starts a second server on the background service's database, and the service can't delete what it creates #14629

Description

@jacobglanz

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/desktop

Steps to reproduce

  1. Fresh Linux box (Ubuntu ARM64, headless). curl -fsSL https://t3.codes/install.sh | sh, t3 service install, t3 connect.
  2. Connect from the Windows desktop app. Have a thread with work going on (it helps if the service writes an event soon after).
  3. In ~, run t3 account (or bare t3). Ctrl-C it after it starts.
  4. In the desktop app, try to delete the new "account" project or its "New thread".

Expected behavior

  • t3 help prints help. t3 account says "unknown command" instead of treating account as a directory.
  • Running t3 while the service owns ~/.t3 doesn't start a second server on the same DB (or refuses, or hands off to the running one).
  • Anything that shows up in the UI can be deleted.

Actual behavior

With the background service running, typing t3, t3 account, t3 login, t3 clients or t3 help in 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 t3 CLI itself, not the desktop app - plus a data point for the #14353 recovery gap.)

  • ~/account, ~/login, ~/clients, ~/help get created, plus a project and empty thread for each (and a project for ~ from bare t3).
  • Delete / archive / settle fail:
Failed to delete thread - Orchestration command invariant failed (thread.delete): Thread '5cd05892-1195-4ed7-8788-023eaabec37b' does not exist for command 'thread.delete'.
Failed to remove "account" - Orchestration command invariant failed (project.delete): Project '25214814-1771-4a90-b044-ecbe6607e897' does not exist for command 'project.delete'.
  • The DB had them: project.created / thread.created events existed, projection_projects / projection_threads rows had deleted_at null, all projectors were at the latest sequence.
  • server-runtime.json was 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.

  1. The CLI default command is start, and the positional <cwd> accepts any word. So t3 help / t3 account start 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).

  2. 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 moved snapshotSequence past 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.sync invariant 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

# Trace log shows each `t3 <word>` booting a full web-mode server (own port, all reactors, `welcome.autobootstrap`), not a CLI subcommand:

21:53:32.830Z server.startup                          server.port=43421   # bare `t3` - service is on 3773
21:53:33.005Z server.startup.welcome.autobootstrap
21:53:33.017Z externalLauncher.launchBrowser          Failure: xdg-open ... (headless box)
21:53:33.045Z orchestration.command.project.create    -> seq 436
21:53:33.071Z orchestration.command.thread.create     -> seq 437
21:53:33.990Z orchestration.command.thread.pull-request.sync (thread cf164c22, owned by the service) -> seq 438
21:54:42.004Z server.startup.keybindings.start        server.port=45777   # `t3 account`

# Event sequence (who wrote what):

436-437  project/thread created      2nd server (`t3`)
438      thread.meta-updated         2nd server's PR reactor, for a service-owned thread
439-442  project/thread created      2nd servers (`t3 account`, `t3 login`)
443-447  pull-request-synced, auto-settle, session-stop/set   service (port 3773)
448-451  project/thread created      2nd servers (`t3 clients`, `t3 help`)
452-453  thread.settled on 449/451   service, from desktop - these worked

# Entities from 436-442 fail every command. Entities from 448-451 work.

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 serve came up at 16:43, thread.delete and project.delete succeeded for all of them (16:47-16:48), and deleted_at is now set in the projection tables. Matches #14353: a restart rebuilds the command model.

Then delete the stray ~/account etc. dirs by hand. Avoid running bare t3 on a box that has the service installed.

Activity

  1. added
    bugSomething is broken or behaving incorrectly.
    needs-triageIssue needs maintainer review and initial categorization.
    on Oct 1, 2026
  2. juliusmarminge commented on Oct 1, 2026

    @juliusmarminge
    Member

    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 after t3 starts a second server against the service's T3 home.

    Why t3 <word> starts a server

    t3 is the server command, and it takes an optional positional <cwd> argument (apps/server/src/bin.ts, sharedServerCommandFlags in apps/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 like account, login, clients, and help are read as a working directory. Help comes from the --help / -h flags, so t3 help doesn't print it. The nearest real commands are t3 connect login and t3 auth.

    resolveServerConfig creates that directory. Web mode, the default, then turns on autoBootstrapProjectFromCwd, so startup adds a project named after the directory with a New thread in it (apps/server/src/serverRuntimeStartup.ts). When port 3773 is taken, findAvailablePort binds an ephemeral port (packages/shared/src/Net.ts), which explains 43421 and 45777 in your trace. The second process also overwrites server-runtime.json with its own pid. On a clean exit it deletes that file without checking whose pid is in it (clearPersistedServerRuntimeState in apps/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.delete and project.delete check 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-memory snapshotSequence. 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. The thread.pull-request.sync rejections 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 project treating 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, but t3 account would still create ./account and 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 t3 or an unknown word on a machine where the service already owns the T3 home. t3 --help prints help.

  3. added
    via-triageFiled through npx t3 triage
    and removed
    needs-triageIssue needs maintainer review and initial categorization.
    on Oct 1, 2026
  4. juliusmarminge commented on Oct 5, 2026

    @juliusmarminge
    Member

    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 help prints help, and plain t3 / t3 start check 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions