windmill-labs/windmill · error
Could not find a free port in the range ${requested}-${reque
Error message
Could not find a free port in the range ${requested}-${requested + MAX_SHIFT - 1}. Stop a holder or pass ${flagLabel} <other>. What it means
resolveBindPort probes ports for the wmill dev servers (frontend/backend, e.g. `wmill dev`/`wmill proxy`). Starting from the requested port it probes up to MAX_SHIFT consecutive ports; if every port in the range requested..requested+MAX_SHIFT-1 is occupied, it throws this Error. The message tells you to free a holder or pass an explicit port via the flag (e.g. --port / --proxy-port).
Source
Thrown at cli/src/utils/port-probe.ts:140
const MAX_SHIFT = 20;
for (let port = requested; port < requested + MAX_SHIFT; port++) {
if (await isPortFreeOnBothStacks(port)) {
if (port !== requested) {
const holder = findPortHolder(requested);
const holderHint = holder
? ` (held by PID ${holder.pid} \`${holder.command}\`)`
: "";
log.warn(
`Port ${requested} is already in use${holderHint}. Using port ${port} instead.`,
);
log.info(
`If you need port ${requested} stable (e.g. a launch.json entry pinned to it), stop the holder and re-run with ${flagLabel} ${requested}.`,
);
}
return port;
}
}
throw new Error(
`Could not find a free port in the range ${requested}-${requested + MAX_SHIFT - 1}. Stop a holder or pass ${flagLabel} <other>.`,
);
}
/**
* The host string we bind to. Explicit IPv4 — `localhost` resolves to
* 127.0.0.1 first on every platform we care about, and binding both stacks
* relies on platform-specific IPV6_V6ONLY behaviour we don't want to debug.
*/
export const BIND_HOST = "0.0.0.0" as const;
View on GitHub (pinned to e474e8803c)
Solutions
- Find and stop the holder: `lsof -i :<port>` or `ss -ltnp | grep <port>`, then kill the stale process
- Pass a different explicit port, e.g. `wmill dev --port 8001` or the flag named in the error message
- Restart Docker/other dev services that are squatting on the port range
- Reboot or clear zombie processes if many ports in the range are leaked
Example fix
// before $ wmill dev # port 8000-... all busy // after $ wmill dev --port 8123
Defensive patterns
Strategy: fallback
Validate before calling
import { createServer } from "net";
function isPortFree(port: number): Promise<boolean> {
return new Promise((res) => {
const s = createServer();
s.once("error", () => res(false));
s.once("listening", () => s.close(() => res(true)));
s.listen(port);
});
}
if (!(await isPortFree(8000))) console.warn("port 8000 busy — pass an explicit --port"); Type guard
null
Try / catch
try {
await wmillDev({});
} catch (e) {
if (String(e.message).includes("Could not find a free port")) {
await wmillDev({ port: 8123 }); // fallback port
} else throw e;
} Prevention
- Before starting dev servers, check `lsof -i :<port>` for stale holders and kill them
- Configure an explicit stable port in launch.json so probing never wanders
- Clean up background wmill dev/proxy processes after each session
- Avoid running multiple worktrees/instances with the same default ports simultaneously
When it happens
Trigger: Calling `wmill dev` or `wmill proxy` (which call resolveBindPort) when the requested port and all MAX_SHIFT-1 fallback ports are already bound by other processes.
Common situations: A previous wmill dev/proxy instance still running in another terminal or leaked after a crash; another service (Docker, webpack, another dev server) squatting on the default ports; multiple worktrees/instances started on the same machine; a launch.json entry pinned to a specific busy port.
Related errors
- Port ${requested} is already in use${holderHint}. Using port
- Dependency generation failed: ${queueResponse.status} ${queu
- Failed to poll dependencies job ${jobId}: ${e?.message ?? e}
- No response body for SSE stream
- Failed to poll flow dependencies job ${jobId}: ${e?.message
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/eac81544b6cb3693.
Report an issue: GitHub.