microsoft/playwright · error · Error
Failed after ${i + 1} attempt(s): ${e}
Error message
Failed after ${i + 1} attempt(s): ${e} What it means
Thrown by the internal retry loop after the request fails on every attempt. Retries only happen on ECONNRESET (connection reset by peer) and only when maxRetries > 0; once the loop reaches its final attempt it reports how many tries were made and the underlying error.
Source
Thrown at packages/playwright-core/src/server/fetch.ts:301
setHeader(headers, 'cookie', valueArray.join('; '));
}
}
private async _sendRequestWithRetries(progress: Progress, url: URL, options: SendRequestOptions, postData?: Buffer, maxRetries?: number): Promise<SendRequestResult> {
const log: string[] = [];
maxRetries ??= 0;
let backoff = 250;
for (let i = 0; i <= maxRetries; i++) {
try {
return await this._sendRequest(progress, log, url, options, postData);
} catch (e) {
if (isAbortError(e))
throw e;
e = rewriteOpenSSLErrorIfNeeded(e);
if (maxRetries === 0)
throw e;
if (i === maxRetries)
throw new Error(`Failed after ${i + 1} attempt(s): ${e}`);
// Retry on connection reset only.
if (e.code !== 'ECONNRESET')
throw e;
const message = ` Received ECONNRESET, will retry after ${backoff}ms.`;
log.push(message);
progress.log(message);
await progress.wait(backoff);
backoff *= 2;
}
}
throw new Error('Unreachable');
}
private async _sendRequest(progress: Progress, log: string[], url: URL, options: SendRequestOptions, postData?: Buffer): Promise<SendRequestResult>{
const fetchLog = (message: string) => {
log.push(message);
progress.log(message);
};View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Verify the endpoint is reachable and stable with a plain curl/node https request.
- Check the server/proxy for connection-reset causes (idle timeouts, keep-alive settings, WAF).
- Increase maxRetries only if resets are genuinely transient, otherwise lower it to surface failures faster.
- Inspect the wrapped error in the message to see if it is an SSL/TLS issue and fix certificates accordingly.
Example fix
// before
await request.get(url, { maxRetries: 3 }); // 3 ECONNRESETs -> throws
// after: retry with backoff in your code AND validate reachability
for (let i = 0; i < 3; i++) {
try { return await request.get(url, { maxRetries: 0 }); }
catch (e) { if (i === 2) throw e; }
} Defensive patterns
Strategy: retry
Validate before calling
// Reachability pre-check (cheap) before the real request
const net = require('net');
const ok = await new Promise(r => {
const s = net.connect(443, new URL(url).hostname).setTimeout(2000)
.on('connect', () => { s.destroy(); r(true); })
.on('error', () => r(false)).on('timeout', () => { s.destroy(); r(false); });
}); Type guard
null
Try / catch
null
Prevention
- Monitor ECONNRESET rates on the target host; treat chronic resets as a server-side defect, not a client one.
- Note: retries are ECONNRESET-only - other errors (DNS, TLS) will not retry.
When it happens
Trigger: Calling request.* with maxRetries set to N>0 while the target server or an intermediary load balancer actively resets the TCP connection (ECONNRESET) on every retry. The final message wraps the last ECONNRESET (or rewritten OpenSSL) error.
Common situations: Unstable upstream host behind a flaky proxy/load balancer; keep-alive connections closed server-side mid-request; TLS/SSL renegotiation resets; high-traffic endpoints that drop connections under load.
Related errors
- Timeout ${options.timeout}ms exceeded
- Malformed endpoint. Did you use BrowserType.launchServer met
- Timeout ${params.timeout}ms exceeded
- 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/f4b008e2edab1caa.
Report an issue: GitHub.