Repository navigation
Desktop on Windows sends its bootstrap token to a WSL background service on the same port (invalid_credential on first launch) #16826
Description
What happened
On Windows, the desktop app (nightly) often shows this error the first time it is opened. Closing it and opening it again usually works.
PrimaryEnvironmentRequestError
at async Object.beforeLoad (t3code://app/assets/main-CRgQa3HH.js:20:72083)
Caused by:
Error: Error invoking remote method 'desktop:get-local-environment-bearer-token': DesktopLocalEnvironmentAuthSessionBootstrapError: Failed to create the local desktop bearer session.
Diagnosis
The desktop sends its bootstrap token to a different T3 Code server: a background service running inside WSL2, which Windows reaches through WSL localhost forwarding on the same port.
- The machine has a T3 Code background service inside WSL2 (Ubuntu):
t3code.service, a systemd user unit runningt3 servewithT3CODE_HOME=~/.t3on the Linux side. It listens on the default port,127.0.0.1:3773. - On a cold start, WSL is still booting when the desktop runs
resolveDesktopBackendPort(apps/desktop/src/app/DesktopApp.ts:75). At that moment127.0.0.1,0.0.0.0and::are all free on 3773, so the desktop picks 3773 and its Windows backend binds0.0.0.0:3773. - About 2 s later, WSL's localhost forwarding starts and
wslrelay.exebinds127.0.0.1:3773on Windows. Windows allows both binds, and a connection to127.0.0.1:3773goes to the more specific listener, which is the relay to the WSL service. - The desktop's
POST http://127.0.0.1:3773/oauth/tokenreaches the WSL service. That service has a different desktop bootstrap secret, so it returnsinvalid_credential.isTransientBearerBootstrapError(apps/desktop/src/backend/DesktopLocalEnvironmentAuth.ts:27) treats that as final, so the request is not retried and the renderer crashes inbeforeLoad. - On the next launch, WSL is already running and the relay already holds
127.0.0.1:3773. The scan skips to 3774 and everything works. That is why it seems to fail only "on first open".
Every launch logged in desktop.trace.ndjson (Oct 4–7, about 20 launches) fits this pattern:
- Every launch where the desktop picked port 3773 failed.
- Every launch on 3774 or 3775 succeeded.
While the bug was happening, Windows showed two listeners on 3773:
LocalAddress LocalPort OwningProcess
127.0.0.1 3773 35824 # wslrelay.exe --mode 2 (started 09:54:11 local)
0.0.0.0 3773 37356 # desktop backend (started 09:54:09 local)
and inside WSL:
LISTEN 127.0.0.1:3773 users:(("node-MainThread",pid=594)) # t3 serve (t3code.service, 0.0.44)
Possible fixes, any one of which would help:
- Have the desktop backend bind
127.0.0.1when network access is off, so a later127.0.0.1bind by another process fails instead of shadowing it. - Check after readiness that the server answering on the chosen port is the one the desktop launched. For example, compare the environment id or PID reported by
/.well-known/t3/environment, then re-select the port or restart the backend if it isn't. - On Windows with WSL, wait for WSL localhost forwarding to settle before scanning, or skip ports a WSL distro is listening on.
- At minimum, report "another T3 Code server is answering on this port" instead of a generic bootstrap failure.
Steps to reproduce
- On Windows 11 with WSL2 (default NAT networking with localhost forwarding), install the T3 Code background service inside a WSL distro with the default port:
t3 service installinside WSL, listening on127.0.0.1:3773. - Run
wsl --shutdown, so the distro and its systemd service start cold the next time WSL is used. - Launch the T3 Code desktop app on Windows. If the WSL backend is enabled, the desktop starts WSL during launch, and the service starts with it.
- The desktop selects port 3773 before
wslrelaybinds it. Its/oauth/tokenexchange getsinvalid_credentialfrom the WSL service, and the error screen appears. - Quit and relaunch. The desktop now selects 3774 and works.
Version
Desktop T3 Code (Nightly) 0.0.46-nightly.20261006.2735 (fd1c338). The WSL background service was t3 0.0.44, and the Windows CLI is 0.0.45.
Environment
Windows 11 Pro 10.0.26300 x64, WSL2 Ubuntu (systemd=true), Node v26.8.2
Evidence
# desktop.trace.ndjson, 2026-10-07 (UTC)
12:54:09.842 resolveDesktopBackendPort -> 3773 ("selected backend port via sequential scan")
12:54:09.852 desktop.backendInstance.start {"id":"primary"}
12:54:10.006..12:54:17.742 GET http://127.0.0.1:3773/.well-known/t3/environment (transport errors, then Success)
12:54:18.330 POST http://127.0.0.1:3773/oauth/token
12:54:18.327 clientRuntime.authorization.bootstrapRemoteBearerSession Failure EnvironmentAuthInvalidError: The environment rejected this client's credentials (invalid_credential).
12:54:18.326 desktop.localEnvironmentAuth.getBearerToken Failure DesktopLocalEnvironmentAuthSessionBootstrapError
(repeated 3x within 600 ms)
# Launch port vs. outcome, desktop.trace.ndjson{,.1..10}
2026-10-04T18:00:07 port 3773 -> BEARER FAIL 2026-10-04T18:00:28 port 3774 -> ok
2026-10-05T12:57:35 port 3773 -> BEARER FAIL 2026-10-05T12:58:02 port 3774 -> ok
2026-10-06T17:35:31 port 3773 -> BEARER FAIL 2026-10-06T17:35:48 port 3775 -> ok
2026-10-06T22:01:20 port 3773 -> BEARER FAIL 2026-10-06T22:01:33 port 3774 -> ok
2026-10-07T12:54:09 port 3773 -> BEARER FAIL
(no launch on 3773 succeeded; no launch on 3774/3775 failed)Related issues
#12918 has the same error screen, but its cause is transient 5xx and reset errors, and its fix retries only those. A rejected credential stays final, so that fix does not cover this case. #6097 covers the desktop and a background service on the same OS sharing a port and database. Here the service runs in WSL with its own T3 home, and the problem is the WSL relay taking over the desktop's port after the port was chosen.
Fix applied or workaround
I moved the WSL service off the default port with a systemd drop-in, which survives t3 update rewriting the unit, and updated it to 0.0.45:
mkdir -p ~/.config/systemd/user/t3code.service.d
printf '[Service]\nEnvironment=T3CODE_PORT=4773\n' > ~/.config/systemd/user/t3code.service.d/port.conf
systemctl --user daemon-reload
t3 update --yesAfterwards only the desktop backend listens on 3773 on Windows. Setting T3CODE_PORT for the desktop app on the Windows side also works.
Filed by
Claude Code (claude-opus-5-5) via t3 triage
Activity
juliusmarminge commentedon Oct 7, 2026
Note
Grok responding on behalf of Julius.
Thanks for the detailed diagnosis. I checked it against the code, and it holds up.
Root cause
resolveDesktopBackendPort(apps/desktop/src/app/DesktopApp.ts:75) picks the first port that is free on127.0.0.1,0.0.0.0and::(DesktopApp.ts:40). It only checks once, at startup, so it can't see a WSL relay that binds the port about 2 s later.127.0.0.1(apps/desktop/src/backend/DesktopServerExposure.ts:110-111). The bind host is127.0.0.1only inlocal-onlymode; any other exposure mode binds0.0.0.0(DesktopServerExposure.ts:113-131). Your0.0.0.0:3773listener means network access was on for this launch. In that mode, a later, more specific127.0.0.1:3773bind bywslrelay.exetakes the desktop's own loopback traffic./.well-known/t3/environmentuntil it gets an HTTP response (apps/desktop/src/backend/DesktopBackendManager.ts:70,381-397). It doesn't check that the server answering is the backend the desktop started.isTransientBearerBootstrapError(apps/desktop/src/backend/DesktopLocalEnvironmentAuth.ts:27-37) retries only fetch errors, timeouts and 502/503/504. The WSL server'sinvalid_credentialis final, so it surfaces asDesktopLocalEnvironmentAuthSessionBootstrapError.Proposed fix
local-onlymode. When it's on, consider binding127.0.0.1as well, or otherwise making sure another process can't shadow loopback./.well-known/t3/environment. On a mismatch, restart on the next free port.Related: #9056, #12919, #7702, #10360.
Workaround
Stop the WSL
t3code.service, or move it to a different port. Then relaunch the desktop app. A second launch also works, because the scan skips the port the relay holds.rhaoio commentedon Oct 21, 2026
Hey guys! just had an issue with t3code on Windows and WSL, below is from t3 triage with Claude. Thanks! (enjoying the software)
Another instance of this race, with a different user-visible symptom: instead of the bootstrap token landing on the wrong server, a saved connection to the WSL
background service stops working because
localhost:3773now resolves to the Windows backend.Setup
T3 Code (Beta)0.1.0 (stable), Windows primary backend on127.0.0.1:3773t3code.servicesystemd user unit inside the distro,t3 serve0.1.0 on127.0.0.1:3773(WSL side), reached from Windows through WSL localhost forwarding,saved in the desktop app as
http://localhost:3773/wslBackendEnabled: true,wslDistro: "Ubuntu-22.04",wslOnlyabsentWhat happened
On a cold start (WSL VM not running), the desktop app's WSL probe boots the VM, systemd starts the service, and the service needs ~8 s to listen. The desktop's
resolveDesktopBackendPortscan runs ~0.2 s after the probe, finds 3773 free, and the Windows backend binds127.0.0.1:3773. When the WSL service startslistening, forwarding to Windows
127.0.0.1:3773cannot be established (the Windows backend already holds it;[::1]:3773is not reachable either).The saved connection then connects to the Windows backend and the client blocks it:
Windows backend trace (
server.trace.ndjson), 09:14 to 09:24 local: 203 + 28ConnectionBlockedErrorwith that message, and the WSL service trace shows zeroinbound Electron requests after the restart. The previous evening, with WSL already running before the app launched, the service held 3773, the Windows backend
scanned to 3774, and the WSL service trace shows ~90 Electron requests per half hour on
localhost:3773.Timeline today:
desktop.wsl.isAvailableprobe at 09:14:12.5), systemd startst3code.serviceselected backend port via sequential scanport 3773 (desktop trace)Listening on http://127.0.0.1:3773ConnectionBlockedError: Connected environment <windows> does not match <wsl>A secondary effect of the same setup: enabling Settings > Connections > WSL backend starts a second
t3 serveagainst the same WSL~/.t3, which exits 78:(
server-child-wsl_Ubuntu-22.04.log, 09:16:17 today and 09:01:00 on Oct 19). Expected given #16102, but it means a user who runs the service has no workingin-app WSL option, and the Connections UI hint ("Stop it, or pair with it instead") points back at the connection that this race breaks.
Workaround that worked
Pin the service to a port the desktop never scans first, via a systemd drop-in, then re-pair:
After this,
http://localhost:3790/.well-known/t3/environmentfrom Windows returns the WSL environment id regardless of startup order, and the managed T3Connect tunnel re-registered on the new port.
Suggestion
Besides the fix discussed above for the token path, the desktop could avoid the collision at the source: when a WSL distro is enabled or probed, wait for the VM
boot (or for forwarding on 3773 to settle) before running the port scan, or default the Windows backend to a port other than the server CLI's
DEFAULT_PORTso a WSL/npmt3 serveon 3773 and the desktop never contend.Diagnosed with
t3 triage(Claude Fable 5.1 via Claude Code).juliusmarminge commentedon Oct 22, 2026
Fresh triage against main b5a226e49c: the literal cold-start sequence stops at
wsl.exe --shutdownon this Linux VM (wsl.exe: command not found), so the Windows relay race was not reproduced here. Current source already binds loopback with network access off; that source check is not runtime proof.The existing fix #21102 targets this failure and still lists native Windows/WSL verification as missing. No competing fix was made. Please provide a cold-start run on a current Windows nightly, the Network access setting, and sanitized desktop/backend startup logs plus the Windows listener table at failure. Please distinguish the original
invalid_credentialfailure from the saved-connection environment-ID mismatch in the newer comment; omit tokens and pairing URLs.