{"record":{"id":"79c748a54b931645","repo":"NanmiCoder/MediaCrawler","slug":"browser-failed-to-start-within-config-browser-lau","errorCode":null,"errorMessage":"Browser failed to start within {config.BROWSER_LAUNCH_TIMEOUT} seconds","messagePattern":"Browser failed to start within (.+?) seconds","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"tools/cdp_browser.py","lineNumber":277,"sourceCode":"                \"browser_data\",\n                f\"cdp_{config.USER_DATA_DIR % config.PLATFORM}\",\n            )\n            os.makedirs(user_data_dir, exist_ok=True)\n            utils.logger.info(f\"[CDPBrowserManager] User data directory: {user_data_dir}\")\n\n        # Launch browser\n        self.launcher.browser_process = self.launcher.launch_browser(\n            browser_path=browser_path,\n            debug_port=self.debug_port,\n            headless=headless,\n            user_data_dir=user_data_dir,\n        )\n\n        # Wait for browser to be ready\n        if not self.launcher.wait_for_browser_ready(\n            self.debug_port, config.BROWSER_LAUNCH_TIMEOUT\n        ):\n            raise RuntimeError(f\"Browser failed to start within {config.BROWSER_LAUNCH_TIMEOUT} seconds\")\n\n        # Extra wait for CDP service to fully start\n        await asyncio.sleep(1)\n\n        # Test CDP connection\n        if not await self._test_cdp_connection(self.debug_port):\n            utils.logger.warning(\n                \"[CDPBrowserManager] CDP connection test failed, but will continue to try connecting\"\n            )\n\n    async def _get_browser_websocket_url(self, debug_port: int) -> str:\n        \"\"\"\n        Get browser WebSocket connection URL\n        \"\"\"\n        try:\n            async with httpx.AsyncClient() as client:\n                response = await client.get(\n                    f\"http://localhost:{debug_port}/json/version\", timeout=10","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/NanmiCoder/MediaCrawler/blob/d6f7c5bb906b6dac40ddf343ef9e26438a3de092/tools/cdp_browser.py#L259-L295","documentation":"Raised by CDPBrowserManager after it spawns the browser process and wait_for_browser_ready() reports the DevTools endpoint still not answering within config.BROWSER_LAUNCH_TIMEOUT seconds. The process was created but never became CDP-reachable — slow start, crash on startup, or a blocked port.","triggerScenarios":"BROWSER_LAUNCH_TIMEOUT too low for the machine (cold start on slow disks/CI); the browser process immediately exits due to a locked/corrupted user-data-dir from a previous crash; sandbox errors in containers (--no-sandbox missing); the debug port occupied by a stale process so the new browser picks another port and readiness polling checks the wrong one.","commonSituations":"CI and Docker where Chrome needs --no-sandbox; leftover chrome zombie processes holding the profile lock and port; first launch on a fresh profile doing heavy migration; low-RAM hosts where Chrome starves during startup.","solutions":["Raise BROWSER_LAUNCH_TIMEOUT (e.g. 60s) in config for slow machines/CI","Kill stale processes and clear the profile lock: pkill -f remote-debugging-port and remove SingletonLock in the user-data-dir","In Docker/CI run the browser with --no-sandbox (or a seccomp profile that allows Chrome's sandbox syscalls)","Free the intended debug port or use another one so readiness polling targets the browser that actually started","Check the browser process stderr — if it exits at once, fix the reported launch error instead of only raising the timeout"],"exampleFix":"# before\nBROWSER_LAUNCH_TIMEOUT = 10\n\n# after\nBROWSER_LAUNCH_TIMEOUT = 60\n# plus, for Docker:\n# launch_browser args include \"--no-sandbox\"","handlingStrategy":"retry","validationCode":"# cheap readiness probe before relying on the manager\nimport urllib.request\n\ndef browser_ready(port: int, timeout: float = 2.0) -> bool:\n    try:\n        with urllib.request.urlopen(f\"http://127.0.0.1:{port}/json/version\", timeout=timeout) as r:\n            return r.status == 200\n    except Exception:\n        return False","typeGuard":null,"tryCatchPattern":"for attempt in range(3):\n    try:\n        ctx = await mgr.init(...)\n        break\n    except RuntimeError as e:\n        if \"failed to start\" not in str(e) or attempt == 2:\n            raise\n        utils.logger.warning(f\"browser slow to start, attempt {attempt+1}\")\n        # clean stale profile locks / raise timeout before retry\n        subprocess.run([\"pkill\", \"-f\", \"remote-debugging-port\"], check=False)","preventionTips":["Set BROWSER_LAUNCH_TIMEOUT generously on CI/Docker (60s+)","Kill zombie chrome processes and remove SingletonLock between runs","In containers add --no-sandbox to browser launch args","Capture the browser process stderr to diagnose instant-exit causes"],"tags":["browser","cdp","timeout","docker","startup"],"backgroundTag":null,"analyzedSha":"d6f7c5bb906b6dac40ddf343ef9e26438a3de092","analyzedAt":"2026-08-15T01:39:07.505Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}