unslothai/unsloth · error · RuntimeError

Could not find a free port in range {start}-{start + max_att

Error message

Could not find a free port in range {start}-{start + max_attempts - 1}

What it means

RuntimeError from the port-picker in run.py: none of the max_attempts candidate ports starting at `start` were free (and, with avoid_own_studio, own-instance collisions abort earlier). Every offset in [start, start+max_attempts-1] was either occupied by another process or failed the bind probe.

Source

Thrown at studio/backend/run.py:826

    host: str,
    start: int,
    max_attempts: int = 20,
    avoid_own_studio: bool = False,
) -> int:
    """Find a free port from `start`, trying up to max_attempts ports.

    ``avoid_own_studio`` aborts rather than skipping past one of our own servers
    in the fallback range, which would start a duplicate on a later port.
    """
    for offset in range(max_attempts):
        candidate = start + offset
        if _is_port_free(host, candidate):
            return candidate
        if avoid_own_studio:
            own = _own_studio_on_port(candidate, host)
            if own is not None:
                _abort_already_running(own, candidate)
    raise RuntimeError(f"Could not find a free port in range {start}-{start + max_attempts - 1}")


from utils.paths.storage_roots import studio_root as _studio_root

# Legacy single-instance file; still read so `stop` finds an older build's server.
_PID_FILE = _studio_root() / "studio.pid"
PID_FILE_GLOB = "studio-*.pid"
# Deliberately not a .pid: everything that globs PID_FILE_GLOB expects a bound
# server with a port in the name, and a process that has not bound yet is not
# one. Only the sibling probe reads these.
STARTUP_MARKER_GLOB = "studio-starting-*.marker"
_OWN_STARTUP_MARKERS: "list[Path]" = []
_STARTUP_MARKER_HOOK_REGISTERED = False


def _pid_file_for_port(port: int) -> Path:
    # PID in the name: 127.0.0.1 and ::1 can share a port, and one file per port
    # would let the second bind overwrite the first.

View on GitHub (pinned to 203007d190)

Solutions

  1. Free or pick a specific port explicitly (pass an explicit --port outside the busy range).
  2. Stop processes holding the range (ss -ltnp 'sport >= START' to identify them).
  3. Widen the ephemeral gap or change the start port if the OS ephemeral range overlaps.
  4. Retry after TIME_WAIT sockets expire (~60s) if a crash storm caused it.

Example fix

# before: range busy
$ unsloth studio run
# RuntimeError: Could not find a free port in range 8000-8009

# after: explicit free port
$ ss -ltn 'sport >= 8000'
$ unsloth studio run --port 8123
Defensive patterns

Strategy: validation

Validate before calling

import socket

def port_free(host: str, port: int) -> bool:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        return s.connect_ex((host, port)) != 0

if not port_free('127.0.0.1', 8000):
    raise SystemExit('port 8000 busy — pass a different --port')

Prevention

When it happens

Trigger: Starting Studio while many ports in the fallback range are taken (other services, a range consumed by ephemeral connections, or several Studio instances already running); a small max_attempts combined with a busy range; Docker port-publishing reserving the span.

Common situations: Dev machine with many local servers; Linux ip_local_port_range overlapping the Studio range so ephemeral sockets randomly occupy candidates; previous crashed runs leaving sockets in TIME_WAIT.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/146830714eaa05cc. Report an issue: GitHub.