{"record":{"id":"47b673da503e5424","repo":"headroomlabs-ai/headroom","slug":"no-available-port-found-in-range-start-port-end","errorCode":null,"errorMessage":"No available port found in range {start_port}-{end_port - 1}","messagePattern":"No available port found in range (.+?)-(.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"headroom/cli/wrap.py","lineNumber":601,"sourceCode":"    return None\n\n\ndef _find_available_port(start_port: int, max_attempts: int = 100) -> int:\n    \"\"\"Find first available port >= start_port via socket.bind probe.\n\n    Skips ports with EADDRINUSE (busy) and EACCES (reserved on Windows,\n    privileged on Linux) — both indicate the port can't be bound here.\n    Other OS errors (EADDRNOTAVAIL) propagate immediately.\n    Raises RuntimeError when no port is found in range.\n    \"\"\"\n    end_port = min(start_port + max_attempts, 65536)\n    for port in range(start_port, end_port):\n        error = _port_bind_error(port)\n        if error is None:\n            return port\n        if error.errno not in (errno.EADDRINUSE, errno.EACCES):\n            raise error\n    raise RuntimeError(f\"No available port found in range {start_port}-{end_port - 1}\")\n\n\ndef _get_log_path() -> Path:\n    \"\"\"Get path for proxy log file.\"\"\"\n    from headroom import paths as _paths\n\n    log_dir = _paths.log_dir()\n    log_dir.mkdir(parents=True, exist_ok=True)\n    return log_dir / \"proxy.log\"\n\n\ndef _get_proxy_stdio_log_path() -> Path:\n    \"\"\"Get path for dedicated proxy stdio capture.\"\"\"\n    return _get_log_path().with_name(\"proxy-stdio.log\")\n\n\ndef _start_proxy(\n    port: int,","sourceCodeStart":583,"sourceCodeEnd":619,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/cli/wrap.py#L583-L619","documentation":"When starting the wrap proxy, Headroom probes a range of ports (start_port up to start_port + max_attempts, capped at 65536) by attempting to bind each one. Ports failing with EADDRINUSE (already bound) or EACCES (privileged/reserved on Linux/Windows) are skipped as unusable; any other OSError (e.g. EADDRNOTAVAIL) propagates immediately. If every port in the range is busy or privileged, this RuntimeError is raised.","triggerScenarios":"Running `headroom wrap <tool>` (or any code path that calls the port-finding helper) on a machine where all ports in the scanned range — typically starting near the requested port, e.g. 4000-4099 — are already bound by other processes, or where the process lacks permission to bind any of them (EACCES on all candidates).","commonSituations":"Many stale Headroom proxy processes left running from previous sessions exhaust the default range; dev machines crowded with other local servers (Vite, Docker, databases) occupying the scanned ports; running as a low-privilege user where the chosen range intersects reserved ports; a low ulimit or socket exhaustion causing binds to fail.","solutions":["Free the occupied ports: find and kill stale proxies, e.g. lsof -i :4000-4099 then kill <pid>, or pkill -f headroom","Pass an explicit port in an empty range, e.g. headroom wrap claude --port 8787","Raise the attempt count / use a different start port so the scan covers a wider or freer range","If EACCES on every port, rerun from an unprivileged high port range (>1024) or grant the process permission to bind"],"exampleFix":"# before\nheadroom wrap claude            # scans default range, all busy -> RuntimeError\n\n# after\npkill -f 'headroom.*proxy'      # clear stale proxies\nheadroom wrap claude --port 8787","handlingStrategy":"validation","validationCode":"import socket\n\ndef find_free_port(start: int, attempts: int = 100) -> int | None:\n    for port in range(start, min(start + attempts, 65536)):\n        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:\n            try:\n                s.bind((\"127.0.0.1\", port))\n            except OSError:\n                continue\n            return port\n    return None\n\nassert find_free_port(4000) is not None, \"No free port in wrap range; kill stale proxies first\"","typeGuard":null,"tryCatchPattern":"try:\n    start_proxy()\nexcept RuntimeError as e:\n    if \"No available port found in range\" in str(e):\n        # retry with an explicit, verified-free port\n        port = find_free_port(20000)\n        start_proxy(port=port)\n    else:\n        raise","preventionTips":["Kill stale headroom proxies before wrapping: pkill -f 'headroom.*proxy'","Pass an explicit --port in a high, rarely-used range","Check occupancy first: lsof -i :<port-range>"],"tags":["network","ports","resource-exhaustion","headroom","cli"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}