unclecode/crawl4ai · critical · Exception
CDP endpoint at {cdp_url} is not ready after startup
Error message
CDP endpoint at {cdp_url} is not ready after startup What it means
When connecting over CDP (config.cdp_url set, or use_managed_browser=True), BrowserManager probes the endpoint with _verify_cdp_ready(cdp_url) before calling playwright.chromium.connect_over_cdp. If the DevTools endpoint does not answer within its retry window, this Exception aborts connection. Note it fires before any Playwright error, so the real state is: nothing is listening / not ready at that URL.
Source
Thrown at crawl4ai/browser_manager.py:889
self._launched_persistent = True
await self.setup_context(self.default_context)
# Set the browser endpoint key for global page tracking
self._browser_endpoint_key = self._compute_browser_endpoint_key()
if self._browser_endpoint_key not in BrowserManager._global_pages_in_use:
BrowserManager._global_pages_in_use[self._browser_endpoint_key] = set()
return
if self.config.cdp_url or self.config.use_managed_browser:
self.config.use_managed_browser = True
if not self._using_cached_cdp:
cdp_url = await self.managed_browser.start() if not self.config.cdp_url else self.config.cdp_url
# Add CDP endpoint verification before connecting
if not await self._verify_cdp_ready(cdp_url):
raise Exception(f"CDP endpoint at {cdp_url} is not ready after startup")
self.browser = await self.playwright.chromium.connect_over_cdp(cdp_url)
contexts = self.browser.contexts
# If browser_context_id is provided, we're using a pre-created context
if self.config.browser_context_id:
if self.logger:
self.logger.debug(
f"Using pre-existing browser context: {self.config.browser_context_id}",
tag="BROWSER"
)
# When connecting to a pre-created context, it should be in contexts
if contexts:
self.default_context = contexts[0]
if self.logger:
self.logger.debug(
f"Found {len(contexts)} existing context(s), using first one",View on GitHub (pinned to 7e80152142)
Solutions
- Start Chrome with debugging enabled and a separate profile: chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug, then point cdp_url at http://localhost:9222.
- Verify the endpoint yourself: curl http://localhost:9222/json/version — it must return JSON before you start the crawler.
- If using the managed browser (no cdp_url), check that the browser binary launched at all (see the 'Failed to start browser' error) and that debugging_port is free.
- Fix host/port in cdp_url (scheme http, correct container hostnames in docker-compose).
Example fix
# before BrowserConfig(cdp_url='http://localhost:9222') # nothing listening -> not ready # after # terminal: google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug # curl http://localhost:9222/json/version # sanity check BrowserConfig(cdp_url='http://localhost:9222')
Defensive patterns
Strategy: retry
Validate before calling
import urllib.request, json
def cdp_ready(cdp_url: str, timeout: float = 2.0) -> bool:
try:
with urllib.request.urlopen(f'{cdp_url.rstrip("/")}/json/version', timeout=timeout) as r:
return r.status == 200 and json.loads(r.read()).get('Browser')
except Exception:
return False Try / catch
try:
async with AsyncWebCrawler(config=BrowserConfig(cdp_url=cdp_url)) as c:
...
except Exception as e:
if 'not ready after startup' in str(e):
# wait for the external Chrome to boot and retry once
await asyncio.sleep(5)
async with AsyncWebCrawler(config=BrowserConfig(cdp_url=cdp_url)) as c:
...
else:
raise Prevention
- Health-check /json/version before creating the crawler when using cdp_url.
- Start external Chrome with --remote-debugging-port and a dedicated --user-data-dir.
- Retry once with backoff to absorb browser boot races.
When it happens
Trigger: config.cdp_url='http://localhost:9222' but no Chrome started with --remote-debugging-port=9222; the managed browser subprocess crashed right after start; CDP bound to 127.0.0.1 while you connect to another host/port; firewall or wrong port; Chrome started with the new --remote-debugging-pipe instead of port.
Common situations: Connecting to an external/pre-launched Chrome that was started without the debugging flag; race where the managed browser is still booting; Docker port-mapping mismatches; Chrome 111+ requiring --remote-debugging-port explicitly plus a non-default user-data-dir.
Related errors
- Failed to start browser: {e}
- Failed on navigating ACS-GOTO: {str(e)}
- Unexpected status code for {url}
- Request timed out: {str(e)}
- Connection failed: {str(e)}
AI-assisted analysis of unclecode/crawl4ai@7e80152142 (2026-08-14).
Data as JSON: /api/errors/e7f59960740aeef5.
Report an issue: GitHub.