# Desktop app stays running windowless after the "T3 home is unavailable" dialog and ignores Quit and SIGTERM · Issue \#23843 · pingdotgg/t3code

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

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

Visibility: public

State: backlog

Priority: 0

## Description

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

---

### What happened

After the "This T3 home is unavailable" dialog (the #22820 `OlderBootServiceError` case), clicking **OK** didn't quit the macOS desktop app. The process stayed alive with no window, no backend child and no listening port, so the app looked stuck:

- Reopening T3 Code from the Dock or Finder did nothing. macOS reactivated the existing instance, but no window opened and no new startup happened.
- **App > Quit** did nothing either.
- `kill <pid>` (SIGTERM) didn't stop it. Only `kill -9` did.

Once the process was killed (and the conflicting service removed), a fresh launch started normally.

### Diagnosis

The dialog is shown by `onStateDirOwned` in `apps/desktop/src/backend/DesktopBackendPool.ts` (~line 179). After the dialog closes, it calls `electronApp.quit`. That emits `before-quit`, and `handleBeforeQuit` in `apps/desktop/src/app/DesktopLifecycle.ts` handles it:

1. It calls `event.preventDefault()`, sets `state.quitting = true`, and runs `requestDesktopShutdownAndWait`, which ends with `shutdown.awaitComplete`. The real `app.quit()` only happens in the `.finally` after that resolves.
2. In this run, the shutdown evidently never completed, so the second `quit` never happened.
3. With `state.quitting` already `true`, every other way out is disabled:
   - `quitFromSignal` (SIGTERM/SIGINT) does `if (wasQuitting) return;` (~line 153), so SIGTERM is silently ignored.
   - The `activate` handler does `if (yield* Ref.get(state.quitting)) return;` (~line 239), so reopening the app opens no window.
   - A second `before-quit` from Cmd-Q / App > Quit goes through the same `preventDefault` path, because `quitAllowed` is only set in the `.finally`.

I couldn't confirm exactly which step of `DesktopShutdown` hangs after a state-dir-owned exit. The backend child had already exited (code 78) before the dialog appeared, and the desktop trace stops a second after startup, likely because buffered spans were lost on SIGKILL. Whatever stalls, there's no timeout or fallback, so the app can only be stopped with SIGKILL.

Suggested direction: give the post-dialog shutdown a timeout (or call `app.exit` directly in the state-dir-owned path, since no backend is running), and don't let `state.quitting` swallow SIGTERM when the earlier shutdown has stalled.

### Steps to reproduce

1. On macOS, have a pre-ownership-lock background service (e.g. 0.0.45 via launchd) set up for `~/.t3`.
2. Update the desktop app to a current Nightly and let it relaunch.
3. The "This T3 home is unavailable" dialog appears. Click **OK**.
4. Observe: the `T3 Code (Nightly)` process keeps running with no window.
5. Remove the conflict (`npx t3@latest service uninstall --base-dir ~/.t3`), then try to reopen the app: no window. Try App > Quit or `kill <pid>`: no effect. Only `kill -9` ends it.

Not verified: whether it happens every time. In the same session an earlier instance (22:54:54) showed the dialog and was gone before the next launch at 22:55:33. It's unknown whether that one exited on its own.

### Version

Desktop: T3 Code (Nightly) `0.1.2-nightly.20261022.3541` (tag `v0.1.2-nightly.20261022.3541`, commit `044f0725`). Conflicting background service: 0.0.45. Triage CLI: 0.1.2.

### Environment

macOS 26 (Darwin 25.6.0), arm64. Desktop app (Nightly) with Local environment on, connecting locally. Background service via launchd (since uninstalled).

### Evidence

`server-child.log` (home path redacted). The backend exited with 78 before the dialog:
```
22:55:35 ERROR OlderBootServiceError: A T3 Code background service set up for ~/.t3 may still run a release without the ownership lock. ... No server was stopped.
22:55:35 backend child process failure output end pid=18899 code=78
```

Process state at 23:00, about 5 minutes after clicking OK:
```
18424  started 22:55:33  /Applications/T3 Code (Nightly).app/Contents/MacOS/T3 Code (Nightly)
```
- Only the main process, renderer, GPU and network helpers were running.
- There was no backend child, nothing listening on 3773/3774, and no `server-runtime.json`.
- `desktop.trace.ndjson` was last written at 22:55 (last span about 22:55:34).

`kill 18424` and then a 3 s wait left the process alive. `kill -9` ended it. A fresh `open -a "T3 Code (Nightly)"` then started its backend normally on port 3773.

### Related issues

- #22820: the dialog that leads here (old vs. current service).
- #20250: the app quits on home-ownership refusal, so quitting is the intended behavior.
- #22983: quitting leaves the desktop's server running. Different symptom, also in the quit path.
- #15139: an earlier report of a background hang that blocked later launches.

### Fix applied or workaround

`kill -9 <pid>` the stuck `T3 Code (Nightly)` process (or Force Quit in Activity Monitor), fix the underlying ownership conflict, then reopen the app.

### Filed by

Claude Code (Claude Opus 5.5) via `t3 triage`.

