Skip to content

Commit 07238df

Browse files
author
wanglu241
committed
fix(ports): fail fast when port availability cannot be verified
ensurePortAvailable routed through checkPortInUse only threw on "busy", silently treating "unknown" (a bind probe that failed with something other than EADDRINUSE, e.g. EACCES/EINVAL) as available. The original bare-listen path rethrew such errors; restore that fail-fast and add an unknown-path regression guard.
1 parent df50b3b commit 07238df

2 files changed

Lines changed: 18 additions & 1 deletion

File tree

src/infra/ports.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ describe("ports helpers", () => {
110110
}
111111
});
112112

113+
it("regression: #94415 — ensurePortAvailable fails fast when availability cannot be verified", async () => {
114+
// An out-of-range port makes every bind probe reject with a non-EADDRINUSE
115+
// error (ERR_SOCKET_BAD_PORT), so checkPortInUse reports "unknown". The
116+
// preflight must rethrow rather than silently treat an unverifiable port as
117+
// free (the pre-refactor bare-listen path rethrew non-EADDRINUSE errors).
118+
await expect(ensurePortAvailable(99999)).rejects.toThrow();
119+
await expect(ensurePortAvailable(99999)).rejects.not.toBeInstanceOf(PortInUseError);
120+
});
121+
113122
it("handlePortError exits nicely on EADDRINUSE", async () => {
114123
const runtime = {
115124
error: vi.fn(),

src/infra/ports.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,17 @@ export async function ensurePortAvailable(port: number): Promise<void> {
3939
// Probe every address family: a bare listen binds the IPv6 wildcard on
4040
// dual-stack hosts and misses an IPv4-only occupant, letting Chrome later
4141
// collide on 127.0.0.1 and surface a misleading CDP 401.
42-
if ((await checkPortInUse(port)) === "busy") {
42+
const status = await checkPortInUse(port);
43+
if (status === "busy") {
4344
throw new PortInUseError(port);
4445
}
46+
if (status === "unknown") {
47+
// A bind probe failed with something other than EADDRINUSE (e.g. EACCES on
48+
// a privileged port, EINVAL on an out-of-range port). The original
49+
// bare-listen path rethrew here; preserve that fail-fast instead of
50+
// silently treating an unverifiable port as available.
51+
throw new Error(`Port ${port} availability could not be verified.`);
52+
}
4553
}
4654

4755
export async function handlePortError(

0 commit comments

Comments
 (0)