pbakaus/impeccable · critical

Error: no free port

Error message

Error: no free port

What it means

Without an explicit `--port`, the live server auto-scans upward from port 8400, incrementing on each failed bind. If every port through u16::MAX is exhausted, it prints this message and exits 1. It indicates the fallback port allocator could not find any bindable port — in practice this signals blocked binds rather than genuine port exhaustion.

Source

Thrown at crates/live/src/live_server.rs:292

            match TcpListener::bind(("127.0.0.1", port)) {
                Ok(l) => l,
                Err(e) => {
                    io.err(&format!(
                        "Error: listen EADDRINUSE: address already in use 127.0.0.1:{} ({})\n",
                        port, e
                    ));
                    return 1;
                }
            }
        }
        None => {
            let mut port: u16 = 8400;
            loop {
                match TcpListener::bind(("127.0.0.1", port)) {
                    Ok(l) => break l,
                    Err(_) => {
                        if port == u16::MAX {
                            io.err("Error: no free port\n");
                            return 1;
                        }
                        port += 1;
                    }
                }
            }
        }
    };
    let port = listener.local_addr().map(|a| a.port()).unwrap_or(0) as i64;

    // Annotation session dir
    let annot_root = live_annotations_dir(&cwd, &env);
    let _ = std::fs::create_dir_all(&annot_root);
    let session_dir = mkdtemp(&jsp::join(&[&annot_root, "session-"]));

    {
        let mut st = lock(&shared);
        st.port = port;

View on GitHub (pinned to 2bc2879276)

Solutions

  1. Check whether binds are blocked at the OS level (sandbox/seccomp/SELinux) — run the same start outside the sandbox.
  2. Inspect system listener count (`ss -ltn | wc -l`); close lingering listeners or restart the network namespace if thousands of sockets are stuck.
  3. Grant the container/user permission to bind loopback ports, or run the live server on the host.

Example fix

// before
$ impeccable live-server
Error: no free port
// after
$ ss -ltn | wc -l            # inspect listener count
$ impeccable live-server     # run where loopback bind is permitted
Defensive patterns

Strategy: fallback

Validate before calling

// sanity-check that loopback binds are permitted at all before starting
python3 -c "import socket; s=socket.socket(); s.bind(('127.0.0.1',0)); s.close()" || { echo 'loopback bind blocked'; exit 2; }

Try / catch

// treat 'no free port' as an environment failure, not a retry loop
try {
  execSync('impeccable live-server');
} catch (e) {
  if (/no free port/.test(String(e.stderr))) {
    throw new Error('loopback binds blocked or exhausted in this environment; fix sandbox/OS policy, then retry');
  }
  throw e;
}

Prevention

When it happens

Trigger: Starting `live-server` without `--port` where the loop `TcpListener::bind(("127.0.0.1", port))` fails for every port from 8400 through 65535, hitting the `port == u16::MAX` guard.

Common situations: A sandbox or security policy (seccomp/SELinux) rejecting every loopback bind, which the scan misreads as 'port in use'; a system leaking thousands of stuck listeners; restricted containers where socket bind is disallowed entirely.

Related errors


AI-assisted analysis of pbakaus/impeccable@2bc2879276 (2026-09-08). Data as JSON: /api/errors/3c6376bab725c4a2. Report an issue: GitHub.