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

  1. Find and stop the holder: `lsof -i :<port>` or `ss -ltnp | grep <port>`, then kill the stale process
  2. Pass a different explicit port, e.g. `wmill dev --port 8001` or the flag named in the error message
  3. Restart Docker/other dev services that are squatting on the port range
  4. 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

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


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/eac81544b6cb3693. Report an issue: GitHub.