Repository navigation
[Bug]: Desktop app hangs in silent background crash loop and blocks subsequent launches if backend fails startup migrations #15139
Copy link
Copy link
Closed as not planned
Closed as not planned
Copy link
Labels
bugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.duplicateThis issue or pull request already existsThis issue or pull request already existsvia-triageFiled through npx t3 triageFiled through npx t3 triage
Description
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
- Have a local
state.sqlitewhereprojection_threadsis missingsettled_at/settled_override(e.g., from an interrupted migration or schema desync where migration033_ProjectionThreadsSettleddid not run). - Launch the desktop application (
T3 Code (Alpha).exe). - The backend server child process fails migration
046_RepairAutomaticSettlementTimestampswithError: no such column: thread.settled_atand exits. - The Electron desktop app never renders a window and does not show an error dialog; instead, it enters an infinite retry loop in the background, repeatedly trying to respawn the backend child process every ~12 seconds.
- While these background processes spin, try launching T3 Code again (via Desktop shortcut or Start Menu). Because the background process holds the single-instance lock (
app.requestSingleInstanceLock()), the new instance forwards the launch request to the zombie instance and terminates immediately. - To the user, the app appears completely dead and unresponsive, silently refusing to open after install.
Expected behavior
- Recovery Dialog / Clean Exit: If the backend child process crashes during startup or fails the HTTP readiness probe (
waitForHttpReady), the desktop app should display an error dialog or crash screen (e.g., notifying the user of the failure and providing a button to inspect~/.t3/userdata/logsor back up and resetstate.sqlite). If unrecoverable, it should release the single-instance lock and terminate rather than lingering as a zombie process. - Defensive Migrations: Migrations such as
046_RepairAutomaticSettlementTimestampsshould defensively verify table columns (e.g.PRAGMA table_info(projection_threads)) before executing rawUPDATEqueries that assume earlier schema alterations succeeded, matching the defensive patterns used in migrations 045, 047, etc.
Actual behavior
No window is ever created. The main process silently loops in the background consuming CPU and holding the Chromium single-instance lock, blocking all future launches until killed manually via Task Manager.
Impact
Blocks work completely
Version or commit
0.0.40 – 0.0.45
Environment
Windows 11 x64, T3 Code Desktop (Electron)
Logs or stack traces
{"message":"backend child process failure output start","level":"ERROR","annotations":{"component":"desktop-backend-child","phase":"START","details":"port=3773"}}
{"message":"backend child process output","level":"INFO","annotations":{"component":"desktop-backend-child","stream":"stdout","text":"ERROR (#5): MigrationError: Migration \"46_RepairAutomaticSettlementTimestamps\" failed\n at runMigrations (resources/server.asar/apps/server/dist/bin.mjs:54374:9) {\n [cause]: effect/sql/SqlError: Failed to prepare statement {\n [cause]: effect/sql/SqlError/UnknownError: Failed to prepare statement {\n [cause]: Error: no such column: thread.settled_at\n at try (resources/server.asar/apps/server/dist/nodeSqliteClient.mjs:479:18)\n }\n }\n}"}}
{"message":"backend child process failure output end","level":"ERROR","annotations":{"component":"desktop-backend-child","phase":"END","details":"code=1"}}Workaround
- Terminate all stuck background processes:
Stop-Process -Name "T3 Code (Alpha)" -Force. - Inspect
~/.t3/userdata/logs/server-child.logfor the exact SQL migration failure. - Either back up and remove
~/.t3/userdata/state.sqliteto let the app generate a fresh database, or manually alterprojection_threadsto add the missing columns (settled_at,settled_override,snoozed_at,snoozed_until) so migration 46 and subsequent migrations can complete.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.duplicateThis issue or pull request already existsThis issue or pull request already existsvia-triageFiled through npx t3 triageFiled through npx t3 triage
Activity
juliusmarminge commentedon Oct 3, 2026
Note
Grok responding on behalf of Julius.
Triage
Thanks @AlonResearch for the detailed write-up and the child-process log! The desktop failure is real, and it's already tracked as #10517 (open). The migration 46 error is what triggered that bug for you, so I've marked this as a duplicate.
Desktop
On 0.0.40 through 0.0.45, and on current
main, a primary backend that exits during startup gets restarted with no limit whiledesiredRunningis set. The delay starts at 500ms and caps at 10s (DesktopBackendManager.ts), which fits the roughly 12-second retries you saw. Nothing on that path shows a dialog, becauseonPreflightFailedonly covers WSL preflight and fatal Electron startup.The main window isn't created until the primary backend is HTTP-ready (
createMainIfBackendReady/waitingForBackendinDesktopWindow.ts), and the connecting splash only appears in WSL-only mode (DesktopApp.ts). On Windows, that means no window at all.The Clerk bridge takes Electron's profile single-instance lock at startup (
DesktopClerk.ts). A later launch isn't the primary instance, so it quits. Thesecond-instancehandler only reveals aBrowserWindowif one already exists. With no window, every later launch does nothing until the stuck process is killed.That's the same gap as #10517. Open PR #10523 targets it: after five primary crashes, it stops retrying and shows a native dialog with the child error plus Retry and Quit. The dialog doesn't need a window, so it would cover this Windows case too.
Migration 46
46_RepairAutomaticSettlementTimestampsupdatesprojection_threads.settled_atand filters onsettled_overridewithout checking that those columns exist. When they're missing, the statement fails, the child exits with code 1, and the desktop loop above starts. That's the failure in your log.An interrupted
033_ProjectionThreadsSettledwouldn't leave the database in this state. The migrator writes the journal row and runs the migration in one transaction, and rolls back if it fails. Migration 46 only runs when the highest recorded id is below 46, so 033 was skipped, not half-applied. The migrator skips every id at or below its high-water mark without comparing names. That's the slot-collision behavior described in #8896 (closed).A
PRAGMAcheck that skips the update wouldn't add the missing columns, and later reads (including the V1 thread importer) still selectthread.settled_at, so they'd fail the same way. Migrations 033, 034, 045, and 047 guard their ownADD COLUMN, not a later repair update. Open PR #9312 re-applies a skipped migration and warns on slot collisions. Open PR #14202 turns an id clash into a single clear startup error instead of a crash loop.On 0.0.40 through 0.0.45 the file is
userdata/state.sqlite. Currentmaincopies it once tostatev2.sqliteand migrates the copy, so the same migration 46 failure can still stop a V2 boot when the copied journal skipped 033. A maintainer will decide on the fix direction.Your workaround is fine for getting unstuck locally: kill
T3 Code (Alpha), then either add the missing columns or start from a fresh database (back upstate.sqlitefirst).t3dotgg commentedon Oct 14, 2026
Note
🤖 Claude Opus 5.5 responding on behalf of Theo
Closing as a duplicate of #10517, as triaged above. The silent restart loop after a backend fails at startup is tracked there. The migration failure that triggered it for you is a separate cause. If it still happens on a current nightly, please open a new issue with the migration error.