Skip to content

[Bug]: Desktop update can't relaunch when an SSH client starts a server during the restart #22990

Open

Description

@ajbarryiii

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. On host A, run T3 Code desktop with an update downloaded and ready to install.
  2. On machine B, connect T3 Code to host A over SSH and leave it connected.
  3. On host A, install the update.
  4. While A's desktop is stopped for the update, B loses its tunnel and reconnects. Its launch script finds no server on A and starts t3 serve against ~/.t3.
  5. A's updated desktop relaunches.

This depends on timing. B has to reconnect during the gap, which was about 8 seconds here.

Expected behavior

The updated desktop relaunches and owns its home. B waits through the update and then reconnects to the desktop's new server.

Actual behavior

The SSH-launched server takes the home's ownership lock first. The relaunched desktop shows "This T3 home is unavailable" and quits. The only way out is to find and stop the SSH-launched server by hand.

The old backend did stop cleanly (1.95 s, well under the 5 s stop timeout), so this is not the slow-shutdown race that the open PRs #22572 and #22345 address.

Impact

Blocks work completely

Version or commit

Updating 0.1.0 → 0.1.1 on the host. The SSH-launched server was 0.1.0.

Environment

Host: macOS 27.0, Apple Silicon, desktop app, no background service. Remote client: T3 Code desktop 0.1.0 on NixOS 26.05 (Linux 6.18), connected over SSH via Tailscale.

Logs or stack traces

Host-local times (UTC-7). host = host's desktop/server traces and ~/.t3/ssh-launch state; client = remote client's own traces.

7:49:51.9  client  EnvironmentSupervisor.connectionLost for the SSH environment
7:49:54    host    updater finishes stopping the desktop backend (1.95 s)
7:49:56.8  client  reconnect: readiness on the old tunnel fails, then
                   ssh/tunnel.launchOrReuseRemoteServer runs the launch script (8.4 s)
7:50:01    host    new server.log under ~/.t3/ssh-launch/<key>/;
                   a 0.1.0 `t3 serve --base-dir ~/.t3` starts
7:50:02    host    updated desktop backend refused: home already owned (onStateDirOwned)
7:50:04    host    SSH-launched server reports ready (headless)
7:50:05.3  client  launch script returns; tunnel to the new server opens

Screenshots, recordings, or supporting files

No response

Workaround

Stop the SSH-launched t3 serve process, then reopen T3 Code.

Relation to #21423

#21423 describes the same lock conflict after a normal quit or reboot. It was closed because the ownership lock is working as designed, and automatically attaching the desktop to an existing server is a feature request (#20250, and #8115 explains why that is a large design). I agree with both points. This report is narrower. During a desktop update, the desktop is guaranteed to come straight back, and the SSH launcher starts a competing server in that short window anyway.

Proposed fix (small, no lock or protocol changes)

  1. Updater: before stopping backends in installDownloadedUpdate, write a short-lived marker, runtime/desktop-update-in-progress, next to the existing desktop-update-restart marker. Remove it wherever that marker is removed (resetInstallAction, recoverFailedInstall). It is a separate file because desktop-update-restart is consumed during shutdown for the T3 Connect tunnel handoff.
  2. SSH launch script: at the point where it would start a new t3 serve because no server was found, check the marker. If it is under 1 minute old, exit with "T3 Code on this host is installing an update; the connection will retry shortly" and start nothing. The 1-minute limit matches DESKTOP_UPDATE_RESTART_MARKER_TTL.

Nothing else changes:

  • The client's connection supervisor already backs off and retries after a failed launch.
  • On a later retry, the launcher's existing branch for adopting a running default-home server picks up the relaunched desktop.
  • The ownership lock, normal quit, crash and reboot behavior, and the client protocol and UI are all untouched.
  • No process is stopped, and the marker expires by itself if the desktop never comes back.

Limitation: only clients running a version with the updated launch script are protected, because the script and the t3 it runs come from the client. Older clients keep today's behavior until they update.

Implementation ready, no PR opened

CONTRIBUTING.md asks that a substantial bug fix link an issue maintainers have triaged, so I haven't opened a PR. The fix is ready on a fork branch, with focused contract tests, in case it's useful: main...ajbarryiii:t3code:fix/defer-ssh-launch-during-desktop-update (one commit on current main, +216/−31 across 6 files, mostly tests). It was written by Claude Opus 5.5 in Claude Code and reviewed by GPT-6.1 Sol in Codex.

If this scope is acceptable, I'm happy to open it as a PR.

Activity

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

    @ajbarryiii
    Author

    I saw #23005 got opened for this. Thanks for addressing it so quickly!

    On the request for logs: here are the host and client logs from the original occurrence, covering the restart gap: https://gist.github.com/ajbarryiii/7546bdde3a0feb81d4897b1b8ac90488. In short, the old backend stopped cleanly at 07:49:54.5. The remote client started its launch script at 07:49:56.8, and the SSH-launched server took the home before 07:50:02.9. The updated desktop was then refused with ServerAlreadyRunningError on that launch and on every relaunch until I stopped that server by hand.

    It's hard to replicate on demand. An update only goes one way, so each attempt needs a new release to install. The remote client also has to reconnect inside the ~8 s restart gap. I'll try to replicate once #23005 lands in a nightly, with both machines on that nightly, and report back.

  3. kraveir0 commented on Oct 23, 2026

    @kraveir0

    Same failure here on 0.1.1 → 0.1.2. Host is macOS on Apple Silicon (desktop app, no background service). The client is T3 Code desktop 0.1.1 on Linux, connected over SSH via Tailscale.

    Host-local times:

    19:16:50  desktop: updates.install writes runtime/desktop-update-restart, backendInstance.stop (3.7 s)
    19:16:59  SSH-launched 0.1.1 `t3 serve --host 127.0.0.1 --port 3773 --base-dir ~/.t3` starts (PPID 1, SSH_CLIENT = the Linux client)
    19:16:59  updated 0.1.2 desktop: backend start -> ServerAlreadyRunningError, code=78
    19:17:26, 19:19:44, ...  every relaunch refused the same way
    

    The gap was about 9 s. As in the original report, the only way out was to stop the SSH-launched server by hand. The client then relaunched another one within seconds, so I had to reopen the desktop immediately to win the race. This happens on every update for me, so I'd love to see #23005 land.

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.needs-triageIssue needs maintainer review and initial categorization.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions