Skip to content

fix(web): desktop window recovers when its backend restarts during a renderer reload - #26612

Open
juliusmarminge wants to merge 4 commits into
mainfrom
fix/issue-26604
Open

juliusmarminge wants to merge 4 commits into
mainfrom
fix/issue-26604

Conversation

@juliusmarminge

Copy link
Copy Markdown
Member

Problem

When the desktop's renderer and local backend are killed together while the Electron main process survives, the window reloads after 500 ms and lands on a fatal "Primary environment request failed during fetch-session-state (HTTP 500)" screen. It stays there until the user clicks Reload, even though the backend is back a few seconds later. On macOS this happens when the user force-quits an app an agent launched. To reproduce on Linux, send SIGTERM to the renderer, GPU and network helpers and the backend child. The window is still on the error screen 30 s later, and the backend was ready again after 3.5 s.

Change

The auth bootstrap already retries for 15 s, and isTransientBootstrapError already meant to retry fetch failures through its TypeError branch. That branch never fires: fetchSessionState and exchangeBootstrapCredential wrap every failure in a PrimaryEnvironmentRequestError inside the retried operation, and fromCause maps a transport failure with no response to status 500. 500 is not in {502, 503, 504}, so the first refused connection was fatal.

isTransientBootstrapError now also treats a PrimaryEnvironmentRequestError as transient when its cause is an HttpClientError whose reason is TransportError (no HTTP response). A real HTTP 500 response still fails at once. The retry budget, the status reported by fromCause and the other callers do not change.

Scope and approval

Fixes #26604

This PR fixes the second half of the issue: the reloaded window never retries. The first half is macOS attributing an agent-launched app to T3 Code's process coalition, so force-quitting that app kills the renderer and backend. That needs native spawn changes and is not part of this PR.

Verification

  • vp test run src/authBootstrap.test.ts src/environments/primary/ (apps/web): 3 files, 17 tests pass. The new test resolveInitialServerAuthGateState > retries the desktop bootstrap while the backend is still unreachable fails on main with the reported chain: PrimaryEnvironmentRequestError … fetch-session-state (HTTP 500), caused by HttpClientError: Transport error, caused by TypeError: Failed to fetch.
  • vp run check:changed: format, lint, knip, typecheck and tests all pass.
  • Built Linux desktop (vp run build:desktop) under Xvfb, fresh T3 home, the same driver on both sides. The driver sends SIGTERM to the renderer, GPU and network helpers and the backend child, which are all descendants of the Electron main PID captured at spawn. Main survives. It records snapshots and DOM text over CDP at +2, +8, +20 and +30 s.
    • Before (886d8f6): fatal screen at +1.4 s. The backend was ready again at +3.5 s, and the window was still on the fatal screen at +30 s.
    • After (3866aa3, same code as the head): the T3 splash shows while the bootstrap retries. The window is back on the app at +8 s and still there at +30 s.
    • Variants on main: a renderer-only kill and a backend-only kill both recover. The failure needs the renderer to reload while the backend is down.
Before 886d8f6, +30 s after kill After 3866aa3, +30 s after kill
before-886d8f6812-desktop-kill-plus30s.png after-3866aa31f3-desktop-kill-plus30s.png

Timelines: before-886d8f6812-timeline.txt, after-3866aa31f3-timeline.txt. Driver: drive.sh, cdp-text.mjs.

Not checked: macOS itself. The coalition kill that triggers this cannot run on Linux. The fixed code path is platform-independent, and this run reproduced it with the same set of killed processes.

Model and harness: Claude Opus 5.5 (claude-opus-5-5) in Claude Code via T3 Code, with Codex gpt-6.1-sol reviewers.

🤖 Generated with Claude Code

juliusmarminge and others added 4 commits October 28, 2026 19:11
…reachable backend

The session request fails with a transport error (no HTTP response) on the
first attempt, as fetch does while the backend is still restarting.
resolveInitialServerAuthGateState() should retry within the bootstrap budget
and resolve; on main it rejects with PrimaryEnvironmentRequestError (HTTP 500).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tarts

A renderer reload while the local backend was down showed the fatal
"fetch-session-state (HTTP 500)" screen on the first connection-refused
attempt. fromCause reports a transport failure with no response as status
500, so the bootstrap retry never saw it as transient. Treat a wrapped
HttpClientError TransportError as transient; real HTTP 500 responses still
fail fast.

Fixes #26604

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ighbours

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Oct 28, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 9176df7

Macroscope's review found this PR approvable — Wrapped transport failures now use the existing bounded bootstrap retry mechanism, allowing recovery when the backend restarts. Authentication requirements and retry limits remain unchanged.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 9e09926 and 9176df7.

Files selected for processing: 3
  • apps/web/src/authBootstrap.test.ts
  • apps/web/src/environments/primary/auth.ts
  • apps/web/test/environmentHttpTest.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.

Walkthrough

The primary bootstrap retry check now treats wrapped HTTP transport errors as transient. A regression test simulates a failed first desktop bootstrap request, then verifies that a later attempt authenticates and exchanges the desktop bootstrap credential once.

Changes: Desktop bootstrap retry

Layer / File(s) Summary
Classify and test transport retries
apps/web/src/environments/primary/auth.ts, apps/web/test/environmentHttpTest.ts, apps/web/src/authBootstrap.test.ts
The transient-error check recognizes wrapped HttpClientError transport failures. The HTTP test helper returns its runtime-backed runner, and the regression test verifies authentication after an initial transport failure.

Priority: Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Possibly related issues

  • pingdotgg/t3code#23378 — Reports a similar bootstrap failure when the backend is unavailable. This change adds retry handling for wrapped transport errors.
  • pingdotgg/t3code#26162 — Reports a first-open bootstrap failure during backend startup. This change addresses a related retry path, though the issue describes a different IPC error wrapper.

Possibly related PRs

  • pingdotgg/t3code#25526 — Both changes add retry handling to authentication-related requests during temporary unavailability.
  • pingdotgg/t3code#21780 — Both changes address desktop startup while the local backend is unavailable.

Suggested labels: bugfix, size:S

Suggested reviewers: t3dotgg, maria-rcks

Merge Risk: Minimal · up to 9176d

Desktop bootstrap retries transport failures; no merge-blocking risk is evident.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the primary change: desktop recovery when the backend restarts during a renderer reload.
Description check Passed The description includes complete Problem, Change, Scope and approval, and Verification sections. It explains the failure, the retry logic change, issue scope, focused tests, desktop validation, limit…
Linked Issues check Passed For directly linked issue #26604, the change makes a wrapped PrimaryEnvironmentRequestError transient when its cause is an HttpClientError with TransportError. It preserves immediate failure for…
Out of Scope Changes check Passed The changes are limited to bootstrap retry classification and supporting test infrastructure. The added test helper return value supports the new retry test. No unrelated product behavior or native pr…
Full details: Description check

Explanation

The description includes complete Problem, Change, Scope and approval, and Verification sections. It explains the failure, the retry logic change, issue scope, focused tests, desktop validation, limitations, and agent details.

Full details: Linked Issues check

Explanation

For directly linked issue #26604, the change makes a wrapped PrimaryEnvironmentRequestError transient when its cause is an HttpClientError with TransportError. It preserves immediate failure for real HTTP 500 responses because the new condition checks the cause type and reason. The added authBootstrap.test.ts test covers an unreachable backend followed by recovery and verifies authenticated bootstrap. The separate macOS process-coalition problem is explicitly outside this PR's stated scope.

Full details: Out of Scope Changes check

Explanation

The changes are limited to bootstrap retry classification and supporting test infrastructure. The added test helper return value supports the new retry test. No unrelated product behavior or native process-spawn changes are included.

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS 0-9 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Force-quitting an app an agent launched on macOS kills the desktop's renderer and backend, and the reloaded window never retries

1 participant