mastra-ai/mastra · error · Error

Timeout resolving WebSocket URL from ${versionUrl} (10s)

Error message

Timeout resolving WebSocket URL from ${versionUrl} (10s)

What it means

resolveWebSocketUrl enforces a hard 10-second timeout (AbortController) on the /json/version HTTP fetch. If the request aborts, the AbortError is converted into this descriptive timeout error. It means the CDP endpoint did not answer in time.

Source

Thrown at packages/core/src/browser/browser.ts:944

        clearTimeout(timeoutId);

        if (!response.ok) {
          throw new Error(
            `Failed to fetch CDP version info from ${versionUrl}: ${response.status} ${response.statusText}`,
          );
        }

        const data = (await response.json()) as { webSocketDebuggerUrl?: string };
        if (!data.webSocketDebuggerUrl) {
          throw new Error(`No webSocketDebuggerUrl found in CDP version response from ${versionUrl}`);
        }

        this.logger.debug?.(`Resolved WebSocket URL: ${data.webSocketDebuggerUrl}`);
        return data.webSocketDebuggerUrl;
      } catch (error) {
        clearTimeout(timeoutId);
        if (error instanceof Error && error.name === 'AbortError') {
          throw new Error(`Timeout resolving WebSocket URL from ${versionUrl} (10s)`);
        }
        throw error;
      }
    }

    // Unknown protocol - return as-is and let the caller handle it
    return url;
  }

  // ---------------------------------------------------------------------------
  // Disconnection Detection & Error Handling
  // ---------------------------------------------------------------------------

  /**
   * Error patterns that indicate browser disconnection.
   * Used by isDisconnectionError() to detect external browser closure.
   */
  protected static readonly DISCONNECTION_PATTERNS = [

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Confirm the endpoint responds with curl <versionUrl> before connecting.
  2. Ensure Chrome is fully started (wait for the port to accept connections) before connecting.
  3. Fix network/firewall rules so the host:port is reachable from the app.
  4. Retry with backoff after startup if the browser may still be booting.

Example fix

// before
await browser.connectToExternalCdp('http://chrome.internal:9222'); // chrome still starting
// after
await waitUntil(() => fetch('http://chrome.internal:9222/json/version').then(r => r.ok), { retries: 10, delayMs: 500 });
await browser.connectToExternalCdp('http://chrome.internal:9222');
Defensive patterns

Strategy: retry

Validate before calling

const port = new URL('http://host:9222').port;
await new Promise<void>((ok, fail) => {
  const s = net.createConnection({ host: 'host', port: +port });
  s.on('connect', () => { s.end(); ok(); });
  s.on('error', fail);
  s.setTimeout(3000, () => { s.destroy(); fail(new Error('CDP port not reachable')); });
});

Try / catch

for (let i = 0; i < 5; i++) {
  try { await browser.connectToExternalCdp(url); break; }
  catch (err) {
    if (err instanceof Error && err.message.startsWith('Timeout resolving WebSocket URL') && i < 4) {
      await new Promise(r => setTimeout(r, 2000)); // Chrome may still be starting
      continue;
    }
    throw err;
  }
}

Prevention

When it happens

Trigger: The http(s) CDP version URL is unreachable — wrong host/port, firewall dropping packets, Chrome not yet listening, DNS black-holing, or an overloaded endpoint — and fetch hangs until the 10s AbortController fires.

Common situations: Connecting before a containerized Chrome finished starting; pointing at a firewalled remote host where packets are silently dropped (hang instead of refuse); typos in hostnames causing long DNS timeouts.

Understand the failure class

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/b8680a19e041fc70. Report an issue: GitHub.