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
- Increase the timeout: connect({ wsEndpoint, timeout: 60_000 }) or omit it to use the default (30s).
- Verify the wsEndpoint is reachable from the client (curl/wscat the WebSocket URL) and that the server process is healthy.
- Check server-side logs/CPU/memory — a slow browser spawn is usually the root cause, not the network.
- 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
- Set timeout proportional to expected cold-start latency (browser spawn can take seconds).
- Health-check the server (TCP/HTTP) before connecting.
- Co-locate client and server when latency is unpredictable.
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Timeout ${options.timeout}ms exceeded
- Malformed endpoint. Did you use BrowserType.launchServer met
- Failed to download ${title}, caused by ${e.stack}
- Socks4 proxy protocol does not support authentication
- Browser does not support socks5 proxy authentication
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/eb7a48c228d55b2d.
Report an issue: GitHub.