microsoft/playwright · error · Error

Timeout ${params.timeout}ms exceeded

Error message

Timeout ${params.timeout}ms exceeded

What it means

Thrown by connectToEndpoint when raceAgainstDeadline reports result.timedOut — the connection/handshake (transport.connect + initializePlaywright + browser materialization) did not complete before the deadline derived from params.timeout. The connection is force-closed and the configured timeout is surfaced verbatim.

Source

Thrown at packages/playwright-core/src/client/connect.ts:73

    if ((params as any).__testHookBeforeCreateBrowser)
      await (params as any).__testHookBeforeCreateBrowser();

    const playwright = await connection!.initializePlaywright();
    if (!playwright._initializer.preLaunchedBrowser) {
      connection.close();
      throw new Error('Malformed endpoint. Did you use BrowserType.launchServer method?');
    }
    playwright.selectors = playwright.selectors;
    browser = Browser.from(playwright._initializer.preLaunchedBrowser!);
    browser._shouldCloseConnectionOnClose = true;
    browser.on(Events.Browser.Disconnected, () => connection.close());
    return browser;
  }, deadline);
  if (!result.timedOut) {
    return result.result;
  } else {
    connection.close();
    throw new Error(`Timeout ${params.timeout}ms exceeded`);
  }
}

export async function connectToEndpoint(parentConnection: Connection, params: channels.LocalUtilsConnectParams, timeout: channels.TimeoutOptions): Promise<Connection> {
  const localUtils = parentConnection.localUtils();
  const transport = localUtils ? new JsonPipeTransport(localUtils) : new WebSocketTransport();
  const connectHeaders = await transport.connect(params, timeout);
  const connection = new Connection(localUtils, parentConnection._instrumentation, connectHeaders);
  connection.markAsRemote();
  connection.on('close', () => transport.close());

  let closeError: string | undefined;
  const onTransportClosed = (reason?: string) => {
    connection.close(reason || closeError);
  };
  transport.onClose(reason => onTransportClosed(reason));
  connection.onmessage = message => transport.send(message).catch(() => onTransportClosed());
  transport.onMessage(message => {

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Increase the timeout: connect({ wsEndpoint, timeout: 60_000 }) or omit it to use the default (30s).
  2. Verify the wsEndpoint is reachable from the client (curl/wscat the WebSocket URL) and that the server process is healthy.
  3. Check server-side logs/CPU/memory — a slow browser spawn is usually the root cause, not the network.
  4. If connecting repeatedly, add a small retry with backoff rather than one large timeout.

Example fix

// before
const browser = await chromium.connect({ wsEndpoint, timeout: 5000 });

// after
const browser = await chromium.connect({ wsEndpoint, timeout: 60_000 });
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight reachability + pick a generous timeout before connect().
import net from 'node:net';
import { URL } from 'node:url';

async function hostReachable(wsEndpoint, ms = 3000) {
  const u = new URL(wsEndpoint);
  return await new Promise(res => {
    const s = net.createConnection({ host: u.hostname, port: Number(u.port || 80) });
    const t = setTimeout(() => { s.destroy(); res(false); }, ms);
    s.on('connect', () => { clearTimeout(t); s.destroy(); res(true); });
    s.on('error', () => { clearTimeout(t); res(false); });
  });
}
if (!(await hostReachable(wsEndpoint))) throw new Error(`server unreachable: ${wsEndpoint}`);

Try / catch

async function connectWithRetry(bt, wsEndpoint, attempts = 3, timeout = 60_000) {
  let lastErr;
  for (let i = 0; i < attempts; i++) {
    try { return await bt.connect({ wsEndpoint, timeout }); }
    catch (e) {
      lastErr = e;
      if (!/Timeout \d+ms exceeded/.test(String(e?.message))) throw e;
      await new Promise(r => setTimeout(r, 1000 * (i + 1)));
    }
  }
  throw lastErr;
}

Prevention

When it happens

Trigger: Calling browserType.connect({ wsEndpoint, timeout }) where any of these exceeds timeout: slow/lossy network to the remote server, server under heavy load, initializePlaymium/preLaunchedBrowser handshake stalled, DNS/TLS handshake delay, or the server is up but the browser subprocess (chromium) is slow to spawn. Also triggered by passing an extremely small timeout value.

Common situations: Connecting across regions or over VPN; server running on resource-constrained CI; a firewall that silently drops packets mid-handshake; default/low timeout used against a slow launchServer target; transient cloud flakiness.

Understand the failure class

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/eb7a48c228d55b2d. Report an issue: GitHub.