NanmiCoder/MediaCrawler · error · RuntimeError
Browser failed to start within {config.BROWSER_LAUNCH_TIMEOU
Error message
Browser failed to start within {config.BROWSER_LAUNCH_TIMEOUT} seconds What it means
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.
Source
Thrown at tools/cdp_browser.py:277
"browser_data",
f"cdp_{config.USER_DATA_DIR % config.PLATFORM}",
)
os.makedirs(user_data_dir, exist_ok=True)
utils.logger.info(f"[CDPBrowserManager] User data directory: {user_data_dir}")
# Launch browser
self.launcher.browser_process = self.launcher.launch_browser(
browser_path=browser_path,
debug_port=self.debug_port,
headless=headless,
user_data_dir=user_data_dir,
)
# Wait for browser to be ready
if not self.launcher.wait_for_browser_ready(
self.debug_port, config.BROWSER_LAUNCH_TIMEOUT
):
raise RuntimeError(f"Browser failed to start within {config.BROWSER_LAUNCH_TIMEOUT} seconds")
# Extra wait for CDP service to fully start
await asyncio.sleep(1)
# Test CDP connection
if not await self._test_cdp_connection(self.debug_port):
utils.logger.warning(
"[CDPBrowserManager] CDP connection test failed, but will continue to try connecting"
)
async def _get_browser_websocket_url(self, debug_port: int) -> str:
"""
Get browser WebSocket connection URL
"""
try:
async with httpx.AsyncClient() as client:
response = await client.get(
f"http://localhost:{debug_port}/json/version", timeout=10View on GitHub (pinned to d6f7c5bb90)
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
Example fix
# before BROWSER_LAUNCH_TIMEOUT = 10 # after BROWSER_LAUNCH_TIMEOUT = 60 # plus, for Docker: # launch_browser args include "--no-sandbox"
Defensive patterns
Strategy: retry
Validate before calling
# cheap readiness probe before relying on the manager
import urllib.request
def browser_ready(port: int, timeout: float = 2.0) -> bool:
try:
with urllib.request.urlopen(f"http://127.0.0.1:{port}/json/version", timeout=timeout) as r:
return r.status == 200
except Exception:
return False Try / catch
for attempt in range(3):
try:
ctx = await mgr.init(...)
break
except RuntimeError as e:
if "failed to start" not in str(e) or attempt == 2:
raise
utils.logger.warning(f"browser slow to start, attempt {attempt+1}")
# clean stale profile locks / raise timeout before retry
subprocess.run(["pkill", "-f", "remote-debugging-port"], check=False) Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Cannot connect to existing browser on port {self.debug_port}
- Crawler is already running
- File not found
- Not a file
- Cannot find available port, tried {start_port} to {port-1}
AI-assisted analysis of NanmiCoder/MediaCrawler@d6f7c5bb90 (2026-08-15).
Data as JSON: /api/errors/79c748a54b931645.
Report an issue: GitHub.