xtekky/gpt4free · error · TimeoutError

CDP call {method} timed out after 30 seconds

Error message

CDP call {method} timed out after 30 seconds

What it means

Raised by CDPSession.call() in g4f/requests/cdp.py when the CDP command was sent over the WebSocket but no matching response (by request id) arrived within a hard-coded 30 second asyncio.wait_for. The pending future is cleaned up in finally, and the TimeoutError surfaces with the method name. Typical causes are a hung renderer, a dead-but-not-closed socket, or a genuinely slow command like a navigation that never finishes.

Source

Thrown at g4f/requests/cdp.py:447

    async def call(self, method: str, **params) -> dict:
        """Call a CDP method and wait for its result."""
        if not self.ws:
            raise RuntimeError("CDPSession is not connected")

        self.id_counter += 1
        req_id = self.id_counter

        fut = asyncio.get_running_loop().create_future()
        self._pending_requests[req_id] = fut

        payload = {"id": req_id, "method": method, "params": params}
        await self.ws.send_json(payload)

        try:
            return await asyncio.wait_for(fut, timeout=30.0)
        except asyncio.TimeoutError:
            raise TimeoutError(f"CDP call {method} timed out after 30 seconds")
        finally:
            self._pending_requests.pop(req_id, None)

    async def wait_for_event(self, method: str, timeout: float = 30.0) -> dict:
        """Wait for a specific CDP event to fire (one-time)."""
        fut = asyncio.get_running_loop().create_future()
        if method not in self._event_handlers:
            self._event_handlers[method] = []
        self._event_handlers[method].append(fut)

        try:
            return await asyncio.wait_for(fut, timeout=timeout)
        except asyncio.TimeoutError:
            self._event_handlers[method].remove(fut)
            raise TimeoutError(f"Timeout waiting for event {method}")

    def add_event_handler(self, method: str, queue: asyncio.Queue):
        """Add a persistent event listener that pushes events to an asyncio.Queue."""

View on GitHub (pinned to 973504e177)

Solutions

  1. Retry the call once — transient browser hiccups often clear.
  2. If it reproduces on a specific navigate/evaluate, test that page manually in the same Chrome to find what hangs (infinite loop, blocking dialog, unreachable subresource).
  3. Restart the shared browser process (kill chrome / let g4f's lock logic recycle it) to recover a frozen instance.
  4. Give the container more CPU/memory; a starved Chrome routinely misses 30s deadlines.
  5. Split very long operations: navigate with a shorter expectation, then poll via evaluate_js instead of one 30s+ call.

Example fix

// before
result = await session.call("Page.navigate", url=slow_page)  # may exceed 30s

// after
for attempt in range(3):
    try:
        result = await session.call("Page.navigate", url=slow_page)
        break
    except TimeoutError:
        if attempt == 2:
            raise
Defensive patterns

Strategy: retry

Try / catch

try:
    result = await session.call("Page.navigate", url=url)
except TimeoutError as e:
    log.warning(f"{e}; restarting browser and retrying once")
    await restart_browser()
    result = await session.call("Page.navigate", url=url)

Prevention

When it happens

Trigger: Calling Page.navigate to a page that never fires load; Runtime.evaluate on a script with an infinite loop; the browser process froze (OOM, GPU hang); the WebSocket dropped silently so responses never arrive.

Common situations: Heavy pages under memory pressure; sandboxed containers with limited CPU so Chrome is starved; long-running Turnstile/Cloudflare challenges exceeding 30s; network flakiness between client and a remote-hosted Chrome.

Understand the failure class

Related errors


AI-assisted analysis of xtekky/gpt4free@973504e177 (2026-08-14). Data as JSON: /api/errors/81928001fb266d3d. Report an issue: GitHub.