Repository navigation
Desktop shows a fatal "HTTP 500" screen when the backend is busy for more than about 10 s after startup, because the bearer bootstrap budget allows only one attempt #23378
Description
What happened
On a launch where the backend had just become ready, the desktop window went straight to "Something went wrong. Primary environment request failed during fetch-session-state (HTTP 500)." Clicking "Reload app" about a minute later loaded the app normally, with nothing else changed.
Diagnosis
The desktop's bearer bootstrap gives up before one slow attempt can be retried. DesktopLocalEnvironmentAuth.getBearerToken (apps/desktop/src/backend/DesktopLocalEnvironmentAuth.ts:99-117) retries transient errors every 500 ms, but wraps the whole retry loop in Effect.timeout(BOOTSTRAP_TRANSIENT_RETRY_TIMEOUT), which is 15 s (line 19). Each /oauth/token attempt has its own 10 s timeout (DEFAULT_REMOTE_REQUEST_TIMEOUT_MS, packages/client-runtime/src/authorization/remote.ts:24). So when the backend is briefly unresponsive, the budget covers one full attempt plus half of a second. The trace shows exactly that, twice: a 10.0 s RemoteEnvironmentAuthTimeoutError, then a retry interrupted at 4.5 s.
The web side does not retry either. readDesktopPrimaryBearerToken (apps/web/src/environments/primary/desktopAuth.ts:12-15) clears its cached promise and rethrows, and the route's beforeLoad turns that into the full-screen error.
The error screen also reports a status the server never sent. PrimaryEnvironmentRequestError.fromCause (apps/web/src/environments/primary/auth.ts:54) uses readHttpApiStatus(cause) ?? 500, so the IPC timeout is shown as "HTTP 500".
Why the backend was unresponsive: right after startup, spans across unrelated subsystems stretched to about 35 s (08:51:02 to 08:51:37), and then to about 28 s (08:51:38 to 08:52:06). event-loop-stalls.ndjson has one sample in this period, at 08:52:07, with a 27 s max delay at 100% use. The second stall lines up with ProjectionStore.readShellSnapshotRows (apps/server/src/orchestration-v2/ProjectionStore.ts:6289), a json_extract query over every thread's payload_json that ran 27 to 28 s, and 15.6 s again at 08:52:25. Unrelated spans (git status, GitHub GraphQL, simple ProjectService.getShell selects) stretched to the same length. I could not identify the operation behind the first stall. The state database is 17 GB, so slow queries are expected here. The bug is that a backend that is slow for 30 s turns into a fatal screen.
Of the 32 commits on main after this build (b5a226e..3b95e78), one touches these files, 6f75bcb (ChatGPT Business usage limits), and it does not change this behavior.
Steps to reproduce
Not reproduced on demand. Any way of making /oauth/token take longer than about 10 s during the desktop's first load should show it, for example a large state database (17 GB here) where readShellSnapshotRows takes longer than 10 s, or pausing the backend process for 20 s right after it reports ready.
Version
0.1.2-nightly.20261021.3499 (desktop app and its bundled backend)
Environment
macOS 26.6.2, MacBook Air M4 (16 GB), desktop app, Local environment on, no background service installed
Evidence
desktop.trace.ndjson, local time UTC+4
08:51:03 clientRuntime.authorization.bootstrapRemoteBearerSession 10.0 s Failure RemoteEnvironmentAuthTimeoutError: .../oauth/token timed out after 10000ms
08:51:14 clientRuntime.authorization.bootstrapRemoteBearerSession 4.5 s Interrupted
08:51:03 desktop.localEnvironmentAuth.getBearerToken 15.0 s Failure DesktopLocalEnvironmentAuthSessionBootstrapError
08:51:19 (second load) same pattern: 10.0 s timeout, retry interrupted at 4.5 s, getBearerToken fails at 15.0 s
server.trace.ndjson, same run
08:50:52 server.startup 9.75 s (ready at 08:51:02)
08:51:37 sqlite <- ProjectionStore.readShellSnapshotRows 28.6 s
08:51:38 sqlite <- ProjectionStore.readShellSnapshotRows 27.4 s
08:51:38 sqlite <- ProjectService.getShell (x20) 27.8-28.4 s
08:52:25 sqlite <- ProjectionStore.readShellSnapshotRows 15.6 s
event-loop-stalls.ndjson
04:52:07Z delayMaxMs 27001 utilization 1
Same endpoints one minute later: GET /api/auth/session 200 in 1.5 ms, POST /oauth/token 415 in 1.5 ms (dummy body).Related issues
#12918 (closed, with #19215 as its duplicate) is the same error chain caused by a transient /oauth/token failure. Its fix, #12919, added the retry described above, but the 15 s overall budget is shorter than what one 10 s attempt plus a retry needs, so a slow backend still reaches the error screen. #23372 is the same install's previous launch, where startup recovery held a write transaction for 11 minutes. That is a different stall: there, readiness never passed.
Fix applied or workaround
Clicked "Reload app" once the backend had settled, about a minute later.
Filed by
claude (claude-opus-5-5) via Claude Code, following the t3 triage playbook
Activity