Repository navigation
fix(desktop): probe the distro's ports before binding the WSL backend - #7702
DraftProducts wants to merge 1 commit into
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 92bd9af158bc0818e8cd00f6af34ffb55f5a290b. Configure here.
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This PR introduces new WSL port-probing functionality (parsing /proc/net/tcp via wsl.exe) and changes the bootstrap flow and port selection logic at multiple points. Despite the 'fix' label, this is a new capability with cross-platform complexity that warrants human review. You can add or adjust custom eligibility rules. Learn more. |
With wsl-only mode enabled, the desktop scanned for a free backend port from the Electron main process — the Windows side — and handed the result to a backend that binds inside the WSL distro. WSL2's localhost forwarding does not reserve a WSL-side listener's port in the Windows port namespace, so a user already running the `t3` service on 3773 inside their distro got a port the scan called free and a backend that died on `listen EADDRINUSE 0.0.0.0:3773` on every restart. DesktopWslEnvironment can now read the distro's own listening ports in one `wsl.exe` round trip (`cat /proc/net/tcp /proc/net/tcp6`, LISTEN rows only). Bootstrap consults it before choosing the port, so every consumer — the renderer origin, the advertised endpoints, the backend's bind — derives from a port that is free on both sides. The dual-mode WSL secondary's scan gets the same treatment. A probe that fails falls back to the previous Windows-only behavior instead of blocking startup. Model: Claude Opus 5 via Claude Code
92bd9af to
4cd11a5
Compare

What Changed
resolveDesktopBackendPortnow takes the set of ports the WSL distro alreadylistens on and skips them before probing the Windows host.
DesktopWslEnvironmentlearns that set in one
wsl.exe -- cat /proc/net/tcp /proc/net/tcp6round trip,collecting the port of every row in state
0A(LISTEN) regardless of bindaddress, since any listener there blocks the backend's
0.0.0.0bind. A probethat fails returns
None, logs a warning naming the fallback, and the scanreverts to today's Windows-only behavior rather than blocking startup.
The probe is budgeted at
PRE_WARM_TIMEOUT(10s) rather than the 5s itsimmediate neighbours in that file use. Those neighbours always run after the VM
is up, whereas in wsl-only mode this is the first
wsl.execall of the sessionand pays the cold start; a warm round trip measures ~100ms here, so the headroom
only applies to a distro that is genuinely slow to boot. The call is guarded by
isAvailable, a cached filesystem check that is hardcoded false off win32, so amachine without WSL never spawns it.
The choice happens in
bootstrap, ahead ofserverExposure.configureFromSettings,because that is the port's only ingress: the renderer origin pinned by
registerDesktopProtocol, the advertised loopback and LAN endpoints, TailscaleServe, and the backend's own bind all derive from it.
resolvePrimaryruns againon every backend restart, so correcting the port there would let it drift away
from an origin that is already registered and cannot be re-pointed.
buildWslPrimaryConfigstill passesbackendExposure.portverbatim.The dual-mode secondary scan in
DesktopWslBackendhad the identical flaw andgets the same treatment, one probe per scan rather than one per candidate port.
Non-WSL primaries pass an empty set and take the existing path unchanged. The
"Connecting to WSL" splash moved ahead of the probe, since the probe is what
cold-starts the VM, and the
quittingcheck that gated the splash now gates theprobe as well — a bootstrap racing a quit does neither.
Tests: eight new cases across
DesktopApp.test.ts(new file),DesktopWslBackend.test.tsandDesktopWslEnvironment.test.ts. A distro-held3773 yields 3774 and is never offered to
canListenOnHost; the dual-modesecondary skips distro-held ports and probes exactly once; the parser collects
LISTEN rows from both address families, ignores an ESTABLISHED row on the same
port, and returns
Noneon garbage; and aTestClockcase pins the probe budgetalong with the degrade-to-unknown contract — still pending at 9s,
Noneat 10s.All eight fail without the fix. Targeted run over eight desktop suites: 81
passing.
apps/desktoptypecheck, lint and formatpass. The two failures already present on
main(Net.findAvailablePortand theProviderRegistrycodex re-probe) reproduce there and are untouched.Why
With
wslOnlyenabled the port is scanned from the Electron main process — theWindows side — and handed to a backend that binds inside the distro. WSL2's
localhost forwarding does not reserve a WSL-side listener's port in the Windows
port namespace, so the two disagree and a Windows probe is not evidence about
the distro.
Anyone who installed the background service inside their distro hits this. The
service holds 3773, the scan logs
selected backend port via sequential scanhaving just declared 3773 free, and the backend dies on
about every 20 seconds, forever, with nothing shown in the app. The renderer
surfaces it as an unhandled
PrimaryEnvironmentRequestErrorthrown from a routebeforeLoad, which points at authentication rather than at a port.Skipping the taken port rather than failing with a better message is the right
call: the app can simply start on 3774, and someone running both the service and
the desktop app wanted both to work.
Not addressed: the probe is not a guarantee, since something can take the port
between the probe and the launch, so a launch-time
EADDRINUSEstill deserves areadable failure — a separate concern.
wslDistroPortsProbedis logged so aprobe that failed is visible after the fact.
Checklist
Changes by Claude Opus 5 running in Claude Code.
Note
Probe WSL distro ports before binding backend to prevent EADDRINUSE
probeListeningPortstoDesktopWslEnvironmentto discover listening TCP ports inside the WSL distro by parsing/proc/net/tcpand/proc/net/tcp6with a 10-second timeout.resolveDesktopBackendPortandscanForWslPortto accept aportsHeldInDistroset and skip those ports during allocation.probeWslListeningPortstimes out or fails, it returnsNone, causing the app to fall back to the previous Windows-only loopback scan which may result inEADDRINUSEinside the distro.Macroscope summarized 4cd11a5.