Skip to content

First launch after a desktop update can start the backend in web mode without the bootstrap secret, so the desktop's own login is rejected (invalid_credential) #17367

Description

@freddy-d

What happened

I'm on T3 Code nightly on Windows. Always when I update the app and restart it, this error appears:

Something went wrong.
Primary environment request failed during fetch-session-state (HTTP 500).

Fully quitting and reopening the app makes it work again. "Try again" and "Reload app" don't help (see #3513).

Diagnosis

On the first launch after an update, the desktop's own backend can start without its desktop bootstrap envelope. It then runs as a plain web mode server with no desktopBootstrapSecret and no desktopBootstrapToken, so it rejects the desktop's credential.

  1. The desktop spawns the backend with --bootstrap-fd 3 and writes the JSON envelope (mode, desktopBootstrapSecret, etc.) to that fd at spawn (apps/desktop/src/backend/DesktopBackendManager.ts:449-507).
  2. The server reads it in readBootstrapEnvelope (apps/server/src/bootstrap.ts:74) with a default 1000 ms Effect.timeoutOption (bootstrap.ts:86, :145). A timeout resolves to Option.none(), and startup continues silently.
  3. With no envelope, mode falls back to "web" (apps/server/src/cli/config.ts:288-294). desktopBootstrapSecret and desktopBootstrapToken are both undefined, so PairingGrantStore neither accepts rotating desktop tokens nor seeds a desktop grant (apps/server/src/auth/PairingGrantStore.ts:330-350).
  4. The desktop's POST /oauth/token with currentDesktopBootstrapToken(secret, now) returns invalid_credential. isTransientBearerBootstrapError (apps/desktop/src/backend/DesktopLocalEnvironmentAuth.ts:27) treats that as final, so it surfaces as DesktopLocalEnvironmentAuthSessionBootstrapError and the renderer's root beforeLoad fails.

Why it happens after updates: the first launch of a freshly installed server.asar is a cold start (no compile cache, and likely Defender scanning new files). On this machine that backend took about 9 s from spawn to initialized services (spawn 23:39:54.8Z, services up about 23:40:03–05Z). My hypothesis is that a long synchronous stretch blocks the event loop past 1 s around the envelope read. When the loop wakes up, the expired timer is handled before the pending pipe read (Node runs timers before poll I/O), so the envelope is dropped even though it is already in the pipe. I couldn't confirm the timeout directly because no readBootstrapEnvelope span appears in the trace. But the result, a desktop-spawned backend running with server.mode: "web", is confirmed, and the other Option.none() paths (fd not stat-able, stream closed before a line) seem unlikely for a pipe the desktop has just written to.

The core problem is that a backend launched with an explicit --bootstrap-fd silently degrades to web mode instead of waiting longer or failing loudly. Possible directions, for maintainers to decide:

  • When --bootstrap-fd is given explicitly, wait much longer for the envelope, or until EOF.
  • Exit with a clear error instead of starting in web mode, so the desktop's supervisor restarts it.
  • Have the desktop verify that the backend reports desktop mode before treating it as ready.

Steps to reproduce

Not deterministic. It depends on how cold the first post-update start is.

  1. Install T3 Code (Nightly) desktop on Windows, using the Windows backend (not WSL-only).
  2. Let it download a nightly update, then click install/restart.
  3. On the first launch of the new version, the app often shows "Primary environment request failed during fetch-session-state (HTTP 500)". In this user's logs it happened on 2 of the last 3 updates.
  4. server.trace.ndjson for that launch shows server.startup.welcome.autobootstrap with server.mode: "web", followed by failing environment.auth.token spans.
  5. Fully quit and relaunch: the backend starts in desktop mode and login succeeds.

It might reproduce more reliably by artificially delaying the server's startup before readBootstrapEnvelope by more than 1 s. I didn't verify this.

Version

Desktop 0.0.46-nightly.20261008.2833 (tag v0.0.46-nightly.20261008.2833, a6ec88f); bootstrap.ts unchanged on main as of 2026-10-08

Environment

Windows 11 Home 10.0.26200 x64, desktop app with local Windows backend on port 3773 (no WSL backend, no background service)

Evidence

# Update installs and app startups (desktop.trace.ndjson)
2026-10-07T20:20:01Z desktop.updates.install
2026-10-07T20:20:40Z desktop.startup          -> FAILED (backend in web mode)
2026-10-07T20:20:57Z desktop.startup          -> OK (manual relaunch)
2026-10-08T13:22:44Z desktop.updates.install
2026-10-08T13:23:23Z desktop.startup          -> OK
2026-10-08T23:39:15Z desktop.updates.install
2026-10-08T23:39:53Z desktop.startup          -> FAILED (backend in web mode)

# Failing launch: desktop side
23:39:54.760Z desktop.backendInstance.start {"id":"primary"}
23:39:54.821Z http.client GET /.well-known/t3/environment  Failure   (backend not up yet)
23:40:06.857Z http.client GET /.well-known/t3/environment  Success
23:40:07.519Z http.client POST http://127.0.0.1:3773/oauth/token
23:40:07.514Z desktop.localEnvironmentAuth.getBearerToken  Failure
  DesktopLocalEnvironmentAuthSessionBootstrapError: Failed to create the local desktop bearer session.
    [cause]: EnvironmentAuthInvalidError: The environment rejected this client's credentials (invalid_credential).

# Failing launch: server side (same traceId 74529fb7…, so the same backend handled the request)
23:40:05.694Z server.startup.welcome.autobootstrap {"server.mode":"web","server.port":3773,"server.host":"default"}
23:40:07.523Z environment.auth.token Failure  EnvironmentAuthInvalidError (invalid_credential)  url.path=/oauth/token
23:40:07.633Z environment.auth.token Failure  EnvironmentAuthInvalidError (invalid_credential)

# Oct 7 failure shows the same pattern
2026-10-07T20:20:50.958Z server.startup.welcome.autobootstrap {"server.mode":"web",...}
2026-10-07T20:20:52.867Z environment.auth.token Failure

# Only one listener on the port; it is the desktop-spawned backend
LocalAddress LocalPort OwningProcess
127.0.0.1    3773      22848   # T3 Code (Nightly).exe ... server.asar\apps\server\dist\bin.mjs --bootstrap-fd 3 (child of the desktop main process)

Related issues

#16826: same invalid_credential symptom, but there a WSL service takes the port via wslrelay.exe; here only the desktop's own backend listens and it reports server.mode web. #3513: why the error screen needs a full quit. #12918/#12919: retries only cover transient 502-504, invalid_credential is final.

Fix applied or workaround

Nothing was changed on the machine. Workaround: when the error appears, fully quit T3 Code (including the tray icon) and reopen it so the backend is respawned.

Filed by

Claude Code (Claude Opus 5.5, claude-opus-5-5) via t3 triage

Activity

  1. juliusmarminge commented on Oct 9, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Thanks for the detailed traces. Triage notes from current main (393ff4c):

    What the code does on a missed envelope

    Why it might miss the window after an update (not verified)

    • The 1 s timer only starts once readBootstrapEnvelope runs, so a slow module load before that point should not count against it on its own. Something has to stall the read after the timer is armed.
    • On Windows there is no /proc or /dev/fd path (bootstrap.ts#L233-L241), so the envelope is read via fs.createReadStream on the pipe fd (L207-L214). Reads on that path go through the libuv threadpool (see the analysis in #14776 for the telemetry fd). On a cold first launch with Defender scanning freshly installed files, threadpool contention or a long synchronous stall, as you describe, could plausibly push the read past 1 s even with the envelope sitting in the pipe. Neither has been reproduced here.
    • No test covers the timeout-then-web-mode fallback; bootstrap.test.ts only exercises the success, unavailable-fd and error paths.

    Desktop side

    Possible fix directions (for maintainers to choose)

    1. Fail closed on the server: when --bootstrap-fd/T3CODE_BOOTSTRAP_FD is set and no envelope arrives, exit nonzero with a clear error instead of starting in web mode, so the desktop supervisor restarts it. Smallest change; it does turn a slow start into a restart loop if the stall repeats.
    2. Wait longer: raise the timeout for an explicit fd (or wait until a line or EOF), possibly with a log line when the envelope is late. On Windows, reading the pipe through net.Socket instead of fs.createReadStream, as #14776 does for telemetry, may remove the threadpool dependency.
    3. Detect the mismatch on the desktop: check the auth descriptor (expecting desktop-managed-local) or treat invalid_credential from the primary local backend as "respawn once" before surfacing the error.

    Options 1 or 2 address the root cause; option 3 is a safety net and could pair with either.

    Related

    • #16826: same invalid_credential symptom, different cause (WSL service on the same port). Not a duplicate.
    • #3513: sticky error UI with no recovery.
    • #12918 / #12919: transient bearer bootstrap retries, which don't cover a rejected credential.
    • #14776 (open): same fs.createReadStream-on-pipe pattern for the telemetry fd.
    • I didn't find an open or merged PR that changes the envelope timeout or the silent web-mode fallback.
  2. added
    bugSomething is broken or behaving incorrectly.
    on Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions