# \[Bug\]: Desktop update can't relaunch when an SSH client starts a server during the restart · Issue \#22996 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/issues/22996)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: backlog

Priority: 0

## Description

[Discussion and activity](https://git.cafe/pingdotgg/t3code/issues/22996)

---

### Before submitting

- [x] I searched existing issues and did not find a duplicate.
- [x] 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

```shell
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: https://github.com/pingdotgg/t3code/compare/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.
