NanmiCoder/MediaCrawler · error · RuntimeError

Cannot connect to existing browser on port {self.debug_port}

Error message

Cannot connect to existing browser on port {self.debug_port} after waiting {timeout}s. Please ensure:
  1. Your browser is running
  2. Remote debugging is enabled (chrome://inspect/#remote-debugging)
  3. The debug port is {self.debug_port} (configure via CDP_DEBUG_PORT)

What it means

Raised by CDPBrowserManager when connecting to an existing (already running) browser: it polls _test_cdp_connection(debug_port) once per second for `timeout` seconds and, never succeeding, reports it could not reach Chrome's DevTools endpoint. The message walks through the three prerequisites: browser running, --remote-debugging-port enabled, and the port matching CDP_DEBUG_PORT.

Source

Thrown at tools/cdp_browser.py:179

        # The user may need time to enable remote debugging or confirm the connection dialog
        timeout = config.BROWSER_LAUNCH_TIMEOUT
        utils.logger.info(
            f"[CDPBrowserManager] Waiting up to {timeout}s for browser CDP connection..."
        )
        connected = False
        for i in range(timeout):
            if await self._test_cdp_connection(self.debug_port):
                connected = True
                break
            if i % 5 == 0 and i > 0:
                utils.logger.info(
                    f"[CDPBrowserManager] Still waiting for browser... ({i}s elapsed) "
                    "Please enable remote debugging: chrome://inspect/#remote-debugging"
                )
            await asyncio.sleep(1)

        if not connected:
            raise RuntimeError(
                f"Cannot connect to existing browser on port {self.debug_port} "
                f"after waiting {timeout}s. Please ensure:\n"
                "  1. Your browser is running\n"
                "  2. Remote debugging is enabled (chrome://inspect/#remote-debugging)\n"
                f"  3. The debug port is {self.debug_port} (configure via CDP_DEBUG_PORT)"
            )

        # Connect via CDP (reuse existing method)
        await self._connect_via_cdp(playwright)

        # Create browser context (reuse existing method, will prefer existing context)
        browser_context = await self._create_browser_context(playwright_proxy, user_agent)
        self.browser_context = browser_context

        utils.logger.info("[CDPBrowserManager] Successfully connected to existing browser")
        return browser_context

    async def _get_browser_path(self) -> str:

View on GitHub (pinned to d6f7c5bb90)

Solutions

  1. Relaunch your browser with the flag: chrome --remote-debugging-port=9222 (match CDP_DEBUG_PORT) and a separate --user-data-dir if needed
  2. Verify the endpoint answers: curl http://127.0.0.1:9222/json/version — if it fails, the browser is not exposing CDP
  3. Align the port: set CDP_DEBUG_PORT to the port your browser actually uses, or vice versa
  4. Kill other Chrome instances holding 9222 with a stale/non-debug profile
  5. If you don't need an existing browser, use the managed-launch path (which starts its own browser) instead

Example fix

# before
chrome  # normal launch, CDP not exposed -> timeout after Ns

# after
google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/cdp-profile &
curl -s http://127.0.0.1:9222/json/version  # sanity check before starting the crawler
Defensive patterns

Strategy: validation

Validate before calling

import aiohttp

async def cdp_alive(port: int) -> bool:
    try:
        async with aiohttp.ClientSession() as s:
            async with s.get(f"http://127.0.0.1:{port}/json/version", timeout=aiohttp.ClientTimeout(total=2)) as r:
                return r.status == 200
    except Exception:
        return False

# before starting the crawler in 'existing browser' mode:
if not await cdp_alive(config.CDP_DEBUG_PORT):
    raise SystemExit(f"start chrome with --remote-debugging-port={config.CDP_DEBUG_PORT} first")

Try / catch

try:
    ctx = await mgr.init(color, headless=False)
except RuntimeError as e:
    if "Cannot connect to existing browser" in str(e):
        # actionable recovery: launch the browser ourselves with the flag
        subprocess.Popen([browser, f"--remote-debugging-port={mgr.debug_port}", "--user-data-dir=/tmp/cdp"])
        ctx = await mgr.init(color, headless=False)  # retry after launch
    else:
        raise

Prevention

When it happens

Trigger: HEADLESS/OFF mode expecting a user-launched browser: Chrome was started normally (no --remote-debugging-port flag), or it listens on a different port than config.CDP_DEBUG_PORT, or the browser exits/crashes during the wait window. The CDP HTTP endpoint http://localhost:<port>/json/version never answers.

Common situations: First-time setup where the user forgets to relaunch Chrome with the debugging flag; CDP_DEBUG_PORT default (9222) clashing with another Chrome instance; SELinux/firewall blocking loopback DevTools; Chrome 111+ refusing remote debugging on the default profile without a dedicated --user-data-dir.

Understand the failure class

Related errors


AI-assisted analysis of NanmiCoder/MediaCrawler@d6f7c5bb90 (2026-08-15). Data as JSON: /api/errors/10d9f304bcdde06a. Report an issue: GitHub.