Desktop app stays running windowless after the "T3 home is unavailable" dialog and ignores Quit and SIGTERM#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. Onlykill -9did.
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:
- It calls
event.preventDefault(), setsstate.quitting = true, and runsrequestDesktopShutdownAndWait, which ends withshutdown.awaitComplete. The realapp.quit()only happens in the.finallyafter that resolves. - In this run, the shutdown evidently never completed, so the second
quitnever happened. - With
state.quittingalreadytrue, every other way out is disabled:quitFromSignal(SIGTERM/SIGINT) doesif (wasQuitting) return;(~line 153), so SIGTERM is silently ignored.- The
activatehandler doesif (yield* Ref.get(state.quitting)) return;(~line 239), so reopening the app opens no window. - A second
before-quitfrom Cmd-Q / App > Quit goes through the samepreventDefaultpath, becausequitAllowedis 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
- On macOS, have a pre-ownership-lock background service (e.g. 0.0.45 via launchd) set up for
~/.t3. - Update the desktop app to a current Nightly and let it relaunch.
- The "This T3 home is unavailable" dialog appears. Click OK.
- Observe: the
T3 Code (Nightly)process keeps running with no window. - Remove the conflict (
npx t3@latest service uninstall --base-dir ~/.t3), then try to reopen the app: no window. Try App > Quit orkill <pid>: no effect. Onlykill -9ends 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:
| 1 | 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. |
| 2 | 22:55:35 backend child process failure output end pid=18899 code=78 |
Process state at 23:00, about 5 minutes after clicking OK:
| 1 | 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.ndjsonwas 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.