{"record":{"id":"052cbdaaf8930ab5","repo":"NanmiCoder/MediaCrawler","slug":"cannot-find-available-port-tried-start-port-to","errorCode":null,"errorMessage":"Cannot find available port, tried {start_port} to {port-1}","messagePattern":"Cannot find available port, tried (.+?) to (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"tools/browser_launcher.py","lineNumber":117,"sourceCode":"            if os.path.isfile(path) and os.access(path, os.X_OK):\n                paths.append(path)\n\n        return paths\n\n    def find_available_port(self, start_port: int = 9222) -> int:\n        \"\"\"\n        Find available port\n        \"\"\"\n        port = start_port\n        while port < start_port + 100:  # Try up to 100 ports\n            try:\n                with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:\n                    s.bind(('localhost', port))\n                    return port\n            except OSError:\n                port += 1\n\n        raise RuntimeError(f\"Cannot find available port, tried {start_port} to {port-1}\")\n\n    def launch_browser(self, browser_path: str, debug_port: int, headless: bool = False,\n                      user_data_dir: Optional[str] = None) -> subprocess.Popen:\n        \"\"\"\n        Launch browser process\n        \"\"\"\n        # Basic launch arguments\n        args = [\n            browser_path,\n            f\"--remote-debugging-port={debug_port}\",\n            \"--remote-debugging-address=0.0.0.0\",  # Allow remote access\n            \"--no-first-run\",\n            \"--no-default-browser-check\",\n            \"--disable-background-timer-throttling\",\n            \"--disable-backgrounding-occluded-windows\",\n            \"--disable-renderer-backgrounding\",\n            \"--disable-features=TranslateUI\",\n            \"--disable-ipc-flooding-protection\",","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/NanmiCoder/MediaCrawler/blob/d6f7c5bb906b6dac40ddf343ef9e26438a3de092/tools/browser_launcher.py#L99-L135","documentation":"Raised by BrowserLauncher._find_available_port() after trying to bind 100 consecutive TCP ports starting at start_port on localhost and finding all occupied. The browser's remote-debugging port must be bindable, so an exhausted port range aborts launch.","triggerScenarios":"start_port is in a busy range — e.g. starting at 9222 while other Chrome instances, prior crashed crawler runs, or other services hold ports 9222-9321. Each iteration binds and releases, so only genuinely occupied (or permission-denied) ports advance the loop.","commonSituations":"Many parallel crawler workers each launching a browser with the same start port; zombie chrome processes from a previous run still holding debugging ports; Docker/CI environments where the ephemeral range or a same-range service occupies the block; ports below 1024 without privileges.","solutions":["Free the stuck ports: kill leftover chrome/chromium processes (pkill -f remote-debugging-port) or the services holding them","Give each worker a distinct start port far apart (e.g. base 9222 + worker_id*200) so ranges never overlap","Check occupancy first: ss -ltnp | grep -E '922[0-9]' to see what holds the range","Raise the retry count or make start_port configurable so a busy block can be skipped"],"exampleFix":"// before\nport = start_port\nwhile port < start_port + 100:\n    ...\n\n// after — per-worker disjoint range\nstart_port = 9222 + worker_id * 200  # ranges never collide","handlingStrategy":"fallback","validationCode":"import socket\n\ndef port_free(port: int) -> bool:\n    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:\n        try:\n            s.bind((\"localhost\", port))\n            return True\n        except OSError:\n            return False\n\nif not any(port_free(p) for p in range(start_port, start_port + 100)):\n    raise SystemExit(f\"port block {start_port}-{start_port+99} exhausted; free ports or change start_port\")","typeGuard":null,"tryCatchPattern":"try:\n    port = launcher._find_available_port(start_port)\nexcept RuntimeError:\n    # fallback: widen the search range before giving up\n    port = launcher._find_available_port(start_port + 1000)","preventionTips":["Assign disjoint port ranges per parallel worker","Reap zombie chrome processes between runs","Check `ss -ltnp` for range occupancy before launching fleets"],"tags":["browser","ports","cdp","concurrency"],"backgroundTag":null,"analyzedSha":"d6f7c5bb906b6dac40ddf343ef9e26438a3de092","analyzedAt":"2026-08-15T01:39:07.505Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}