viewer: probe port availability by binding, not connecting - #336
Merged
Conversation
The launcher's port_is_free connected to the port and counted only ConnectionRefusedError as free. On Windows a connect to a closed port routinely fails some other way (Hyper-V/WSL port exclusions, loopback filtering, refusals arriving as timeouts), so every port read as occupied and the launcher refused to start with a false "already in use" error. Found by #335's Windows smoke test, where four random ports Python had just bound and released all failed the connect probe. Bind instead: it is the same operation the server is about to perform, so the probe cannot disagree with reality. EADDRINUSE and EACCES (Windows's answer for excluded port ranges) keep the friendly rerun-with---port message; any other error counts as free, because the probe exists only for that message and the server's own bind stays authoritative. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
earthtojake
pushed a commit
that referenced
this pull request
Aug 26, 2026
Source ref: develop Source commit: cce04de Target branch: main Previous target: 8f9a7d7 Release base: 8f9a7d7 Previous source: 96675ba Included commits since previous source: cce04de Merge pull request #337 from earthtojake/release/0.4.28 c3f3856 Release 0.4.28 c7e2a7c Merge pull request #305 from warun7/fix/viewer-worker-deadlock-and-timeouts 2b65d4f Merge branch 'develop' into fix/viewer-worker-deadlock-and-timeouts 6f0265d Merge pull request #335 from warun7/fix/skill-remediations-and-coverage 1e4aea1 Merge branch 'develop' into fix/skill-remediations-and-coverage 1f75ced Merge pull request #336 from earthtojake/claude/port-probe-bind 3236a5c viewer: probe port availability by binding, not connecting 99a806f tests: pick viewer-smoke ports outside the ephemeral range 5633b65 tests: call the module-level drain helper directly 788bb5d tests: retire a busy candidate port instead of failing the viewer smoke 7306fbe tests: skip the cadgen probe in the viewer start smoke, surface its output 603e812 tests: resolve npm through PATH for the viewer start smoke on Windows 0b64fa3 skills: point gcode at the real cad export CLI; cover cad-viewer; fix skill deps 24e9d28 viewer: restore run_cadgen_cold's terminal error return 3150457 tests: drive the stderr drainer from a real subprocess pipe dbeea4f viewer: kill the CAD worker and cold subprocess on idleness, not wall clock 06bf1b3 viewer: add worker and cold process timeouts and stream large assets
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The launcher's
port_is_freeprobed by CONNECTING to the port, and counted onlyConnectionRefusedErroras free. On Windows a connect to a closed port routinely fails some other way: Hyper-V/WSL port exclusions, loopback filtering, refusals arriving as timeouts. Every one of those fell intoexcept OSError: return False, so every port read as occupied and the launcher refused to start with a false "already in use" error.#335's Windows smoke test found this: four random ports that Python itself had just bound and released all failed the connect probe, including ports inside the range its
_free_porthelper pre-verified as bindable. The test-side workarounds in that PR ruled out every innocent explanation and left the launcher's probe as the cause.Fix
Probe by binding, the same operation the server is about to perform, so the probe cannot disagree with reality:
EADDRINUSE(andEACCES, Windows's answer for its excluded port ranges) keeps the friendly "rerun with--port <n>" message.skills/cad-viewer/scripts/vieweris a symlink to this file, so the skill copy is covered by the same edit.Tests
Four new probe-semantics tests in
viewer/server_py/tests/test_start_viewer.py:OSErrors (timeout, reset) read as free — the exact Windows failure shapeEADDRINUSE/EACCESstill blockVerified locally: 15/15 launcher tests pass, the live launch smoke (
scripts/test/test-viewer-launch.sh) passes, and a genuinely occupied port still exits 1 with the friendly message.Unblocks #335 (its Windows run fails only on this probe).
🤖 Generated with Claude Code