Skip to content

fix(dev): treat a wildcard listener as holding the port - #3704

Merged
kwakayama merged 1 commit into
mainfrom
fix/dev-port-probe-wildcard
Aug 14, 2026
Merged

fix(dev): treat a wildcard listener as holding the port#3704
kwakayama merged 1 commit into
mainfrom
fix/dev-port-probe-wildcard

Conversation

@kwakayama

@kwakayama kwakayama commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Problem

veryfront dev prints Ready — http://localhost:3000 and then serves a different process's app on that URL.

Reproduced on macOS with an unrelated dev server already running:

Process Bind Result
other dev server (bare listen(3000)) *:3000 (::, dual-stack) holds the port
veryfront dev 127.0.0.1:3000 binds anyway, no warning

http://localhost:3000 resolves to ::1 first, so the browser reaches the other process. http://127.0.0.1:3000 reaches the veryfront app. Neither the port fall-forward nor the Port N is in use warning fired.

Cause

PROBE_HOSTNAMES checks only the two specific loopback addresses. BSD and macOS permit binding a more specific address over a wildcard holder, so both probes report free:

holder bound on :::3999
probe 127.0.0.1:3999 -> free      ← miss
probe ::1:3999       -> free      ← miss
probe 0.0.0.0:3999   -> in-use
probe :::3999        -> in-use

#3562 added fall-forward; #3650 widened the probe from IPv4-only to both loopback families. Both addresses #3650 added are specific, so a wildcard holder — which is what any bare listen(port) creates — is still invisible.

Fix

Probe the two wildcards alongside the two loopback addresses. A port counts as available only when nothing holds it on any of them.

Testing

Test first, and it failed for the right reason before the change:

skips a port held on a wildcard address ... FAILED
  AssertionError: a listener on :::61774 must count as holding that port
  -   true
  +   false

After: ok | 1 passed (24 steps) | 0 failed. Full CLI suite: ok | 324 passed (3284 steps) | 0 failed. deno lint and deno check clean.

Verified against the live collision that prompted this, with the real listeners still up:

isPortAvailable(3000)   = false
findAvailablePort(3000) = 3001

Notes for review

  • Deliberate over-trigger. Probing the wildcards can also report a port busy when a listener holds an unrelated non-loopback address (e.g. 192.168.1.5:3000). That falls forward and prints Port 3000 is in use, using 3001 instead, which is the safe direction — the alternative is the silent collision above.
  • Other consumer. clearLocalCachesIfPortFree (cli/commands/dev/command.ts:148) shares this probe. The broadening only makes it skip clearing caches while another server holds the port — also the safe direction.
  • Not fixed here. cli/commands/dev/command.ts:258 hardcodes http://localhost:${boundPort} rather than deriving the URL from the bound address. It stops being user-visible once fall-forward works; worth a separate change.
  • The PROBE_HOSTNAMES doc comment is condensed rather than extended; the rationale from fix(dev): probe both loopback families for port availability, and de-flake its test #3650 is preserved in shorter form.

Summary by CodeRabbit

  • Bug Fixes
    • Improved development server port availability checks.
    • Ports occupied on IPv4 or IPv6 wildcard addresses are now correctly reported as unavailable.
    • Added graceful handling for environments without IPv4 or IPv6 support.

`veryfront dev` probes 127.0.0.1 and ::1 to decide whether a port is free.
A server that listens without naming a host binds `::` or `0.0.0.0`, and BSD
and macOS let a more specific address bind over a wildcard holder - so both
loopback probes came back free, `findAvailablePort` returned the busy port,
and the dev server bound 127.0.0.1:3000 beside the existing listener instead
of falling forward.

Nothing failed loudly. Two processes ended up on port 3000, and the
`http://localhost:3000` the CLI printed resolved to whichever family the
resolver returned first - so the URL it had just printed served the other
process.

Probe the two wildcards alongside the two loopback addresses. A port counts
as available only when nothing holds it on any of them.
@github-actions

Copy link
Copy Markdown

📦 Client bundle boundary

Entrypoint Modules Source size Server leaks
src/index.client.ts 454 3062 KiB ⚠️ 39 known

A server module in a client graph aborts hydration in the browser. New leaks fail CI; known leaks are tracked in scripts/lint/client-bundle-baseline.json to burn down.

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 797d1c4d-6153-403d-b24d-8333bc559b14

📥 Commits

Reviewing files that changed from the base of the PR and between 4f80a0b and 9ed1fce.

📒 Files selected for processing (2)
  • cli/commands/dev/port-fallback.test.ts
  • cli/commands/dev/port-fallback.ts

📝 Walkthrough

Walkthrough

Port availability probing now checks IPv4 and IPv6 loopback and wildcard addresses. Tests verify wildcard listener detection and skip unsupported address families.

Changes

Port probing

Layer / File(s) Summary
Expand probed addresses
cli/commands/dev/port-fallback.ts
isPortAvailable now probes IPv4 loopback, IPv6 loopback, IPv4 wildcard, and IPv6 wildcard addresses.
Validate wildcard listeners
cli/commands/dev/port-fallback.test.ts
The test helper accepts arbitrary addresses. Tests verify that IPv4 and IPv6 wildcard listeners make a port unavailable and skip unsupported address families.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 9ed1f

The change makes wildcard listeners count as occupying a port and includes passing focused and full CLI checks; no actionable merge-blocking risk remains beyond normal review.

Possibly related PRs

Suggested reviewers: kojiwakayama

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: port probing now treats wildcard listeners as occupying the port.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dev-port-probe-wildcard

Comment @coderabbitai help to get the list of available commands.

@kwakayama
kwakayama added this pull request to the merge queue Aug 14, 2026
Merged via the queue into main with commit 367970f Aug 14, 2026
34 checks passed
@kwakayama
kwakayama deleted the fix/dev-port-probe-wildcard branch August 14, 2026 11:29
@kojiwakayama kojiwakayama mentioned this pull request Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant