Files
codegraph/docs
Colby Mchenry 69ee3b9a85 fix(ui): fall back past a port Windows refuses with EACCES, and say why (#2299) (#2353)
On Windows, a port another program holds exclusively (SO_EXCLUSIVEADDRUSE)
or one inside an excluded port range fails listen() with EACCES rather than
EADDRINUSE. The `codegraph ui` port fallback only moved on after EADDRINUSE,
so it stopped at the first such port. The error then blamed the POSIX
privileged-port rule ("Ports below 1024 usually need elevated privileges")
for ports like 4747 or 49912. Confirmed on Windows 11: a .NET listener on
0.0.0.0:P with ExclusiveAddressUse makes Node's listen(P, '127.0.0.1') fail
with EACCES.

Fix:
- shouldTryNextPort moves past EADDRINUSE on every platform. It moves past
  EACCES at any port on Windows, which has no privileged ports, and from
  1024 up elsewhere.
- Below 1024 off Windows, EACCES is the privileged-port rule, and the next
  port falls under it too. The walk stops there with the message it always
  gave, rather than trying 20 privileged ports.
- describeBindFailure takes the platform:
  - Windows names the exclusive hold or reserved range and how to list the
    ranges (netsh int ipv4 show excludedportrange protocol=tcp).
  - POSIX keeps the privileged-port text below 1024. Above it, it says the
    system refused permission.
  - A walk that ran out says "in use or reserved" (Windows) or "in use or
    not allowed" when any port was refused rather than in use.
- Both helpers take the platform as a parameter, like browserOpenCommand,
  so the tests check every platform's answer on any host.

This builds on PR #2319 by @sx4im, which advanced on EACCES, used the
Windows wording and added the issue's listen-spy test. That PR advanced on
EACCES on every platform and printed "Windows refused port N ... netsh" on
Linux and macOS too. On POSIX it would also walk 20 privileged ports and
end with "Ports 80-99 are all in use or reserved". This change restricts
both to where they are true. The changelog entry goes in the viewer-launch
file, because the viewer is not released yet.

Verification:
- __tests__/ui-server.test.ts gains three kinds of test:
  - The issue's end-to-end test: port N is taken and N+1 is refused through
    a listen spy, so the server must land past N+1 and serve 200.
  - A pinned-port message test.
  - Unit tests of the decision and the messages for win32, linux and darwin.
- Results on __tests__/ui-server.test.ts:
  - Main source: 9 failed / 40 passed. The walk fails with "Not allowed to
    listen on port 57423. Ports below 1024 usually need elevated privileges".
  - With the fix: 49 passed / 2 skipped (existing POSIX-gated tests).
- Built CLI on Windows, with 0.0.0.0:<port> held exclusively:
  - `codegraph ui` while 4747 is held: main exits 1 with the privileged-port
    message. With the fix it serves on 4748 (GET / 200).
  - `codegraph ui --port <held>`: prints the Windows message.
  - A 3-port walk over held ports reports "in use or reserved".
  - Re-listening on the same server after a real EACCES works on Node 22.22
    and 24.16.
- tsc --noEmit is clean and npm run build succeeds. The 20 viewer test
  files pass (439 passed, 5 skipped).
- Full suite: 5732 passed, 10 failed, 319 skipped. The 10 failures are
  timeouts and EBUSY teardown under load in function-ref,
  git-index-currency, index-daemon-rebuild and rust-self-owner. All four
  files pass when run alone.

Issue #2299 reported by @ijbranch.

Co-authored-by: Saim Shafique <msaimshafique4664@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 19:03:03 +00:00
..