How to Check Why a Puppeteer Request Failed
Log failed request URLs and error text, distinguish network failures from HTTP errors, and trace navigation timeouts and environment issues in Puppeteer.
To find out why a Puppeteer request failed, register a requestfailed listener before the action that triggers it, then log the request URL and request.failure()?.errorText. Check HTTP response status separately: a 404 or 503 usually emits requestfinished, not requestfailed. For a rejected navigation, inspect the URL, SSL, timeout, server reachability, main resource, and any URL access rules.
This guide uses Puppeteer’s documented request lifecycle and navigation behavior. HTTPRequest.failure(), the API reference, Frame.goto(), the troubleshooting guide, and WaitForOptions are the primary references.
1. Log the failed request
Install the listener before navigation or the page interaction that could trigger the request. The failure text may be absent, so always retain the URL and handle a missing failure object or error string.
page.on('requestfailed', request => {
const failure = request.failure();
console.error('Request failed:', request.url(), failure?.errorText ?? '(no failure text)');
});
This is the smallest useful diagnostic. In a service, send these fields to your structured logger along with a capture or job identifier and timestamp, so you can correlate the event with the triggering action and application logs. Avoid logging secrets embedded in query strings or headers; redact sensitive values before storing logs.
2. Tell transport failures from HTTP errors
Puppeteer’s request lifecycle is request, followed by either requestfinished or requestfailed. The latter indicates the request did not complete successfully at the loading/transport level. An HTTP error response is still a completed HTTP exchange: 404 and 503 responses normally lead to requestfinished. Therefore, use response events and status codes to investigate an unsuccessful page response.
page.on('response', response => {
const status = response.status();
if (status >= 400) {
console.error('HTTP error response:', status, response.url());
}
});
page.on('requestfailed', request => {
const failure = request.failure();
console.error('Loading failure:', request.url(), failure?.errorText ?? '(no failure text)');
});
A site returning a branded error page with status 404 is not necessarily a browser network failure. Conversely, a failed stylesheet, image, or analytics call can emit requestfailed while the main document still renders. Decide whether the failure is relevant by looking at the URL and resource’s role.
3. Diagnose a rejected page.goto()
A rejected page.goto() is a navigation problem, which may or may not be explained by a particular subresource event. Puppeteer documents causes including an SSL error, invalid target URL, exceeded navigation timeout, unreachable or unresponsive server, failed main resource, or a URL blocked by configured allowlist/blocklist rules. When a navigation resolves through redirects, its response is for the final redirect destination.
const target = 'https://example.com';
page.on('requestfailed', request => {
const failure = request.failure();
console.error('Request failed:', request.url(), failure?.errorText ?? '(no failure text)');
});
page.on('response', response => {
if (response.status() >= 400) {
console.error('HTTP status:', response.status(), response.url());
}
});
try {
const response = await page.goto(target, {
waitUntil: 'domcontentloaded',
timeout: 30_000,
});
console.log('Navigation response:', response?.status(), response?.url());
} catch (error) {
console.error('Navigation rejected:', error.message);
}
Start by confirming the target URL is valid and reachable from the same runtime where Chromium runs. Then correlate the rejected navigation with failed requests and response status logs. If you use URL allowlists or blocklists in your environment, confirm the destination and redirect targets are permitted.
4. Identify which timeout expired
Navigation, selector waits, and other operations can each have their own timeout. Puppeteer’s wait options document a 30,000 ms default, with 0 disabling that timeout; page timeout methods can change defaults. Increasing a timeout without identifying the operation can hide the symptom and make jobs take longer.
try {
await page.goto('https://example.com', {
waitUntil: 'domcontentloaded',
timeout: 45_000,
});
} catch (error) {
console.error('Navigation timed out or failed:', error.message);
}
try {
await page.waitForSelector('[data-ready="true"]', { timeout: 10_000 });
} catch (error) {
console.error('Readiness selector did not appear:', error.message);
}
Keep navigation and post-navigation waits separately identifiable in logs. A page can navigate successfully and then fail to produce an application-specific selector. That is different from the main document failing to load.
5. Make failures diagnosable in production
- Register listeners early. Attach listeners before
goto()and before clicks or scripts that initiate requests. - Record stable context. Log URL, error text when present, job identifier, operation name, and elapsed time. Redact tokens and personal data.
- Separate event types. Record failed loads, HTTP status responses, navigation rejections, and selector timeouts as distinct categories.
- Keep the useful request scope. Subresource failures may be expected or irrelevant. Flag main document and required API requests distinctly from optional images, ads, or analytics.
- Compare environments. If it only fails in CI, a container, or cloud runtime, inspect browser installation and environment-specific troubleshooting before changing application logic.
- Retry selectively. Retry transient network or server failures with a bounded attempt count and backoff. Do not retry invalid URLs, access-rule blocks, or deterministic application errors indefinitely.
6. Common causes and fixes
| Symptom | Likely area to inspect | Next step |
|---|---|---|
requestfailed with error text |
Request loading or transport | Preserve URL and text; check reachability, TLS, and whether the resource is required. |
requestfailed without error text |
Same, but browser supplied no reason string | Keep the URL, correlate timestamp with browser and application logs, and inspect related navigation events. |
404 or 503 but no requestfailed |
HTTP response status | Inspect the response event and status; the request can still finish successfully at the HTTP exchange level. |
page.goto() rejects immediately |
Malformed URL, SSL, access rules, or unreachable server | Validate the URL and runtime network access; review SSL and allowlist/blocklist policy. |
| Navigation timeout | Navigation wait condition or slow main document | Identify the timed-out operation. Choose a suitable waitUntil condition and adjust only its timeout if needed. |
| Navigation succeeds, later wait fails | Selector or application readiness condition | Check selector correctness and page state; tune the selector wait independently of navigation. |
| Works locally, fails in Linux CI/container | Browser installation or runtime constraints | Use Puppeteer’s environment-specific troubleshooting guide. Its examples include blocked browser downloads, HTTPS-first behavior, Linux sandbox/AppArmor launch issues, and Alpine Chromium compatibility. |
Do not apply a broad workaround such as disabling Chromium’s sandbox without understanding the security tradeoff. Match the fix to the actual runtime error and deployment environment.
7. Performance, reliability, and cost
Event listeners add little diagnostic complexity, but logging every request from a page with many third-party resources can create noisy and expensive logs. Filter or sample routine successful traffic; retain failed request URLs and relevant response statuses. Keep logs bounded and redact credentials, cookies, signed query parameters, and personal data.
For reliability, use a deadline across the whole job as well as operation-specific timeouts, and limit retries. A longer navigation timeout can accommodate genuinely slow pages, but it also holds browser capacity longer when a server is unresponsive. If failures cluster in one environment, compare browser version, installation, network policy, and container configuration before attributing the issue to the target site.
The sources cited here publish no benchmark or named failure-rate statistic. Measure your own workload: navigation duration, timeout frequency, failed main-document requests, and HTTP error rates are useful operational signals.
8. Or skip the browser setup
For screenshot jobs where you want a finished image without installing and operating Chromium, ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. A GET request returns a screenshot or PDF; the API reports page verdict and billing information in response headers. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
9. FAQ
Does a failed image request mean the whole page failed?
No. A subresource can fail while the main document renders. Check its URL and whether your task depends on that resource.
Why did navigation resolve when the site returned an error page?
An HTTP error status can still be a completed response. Inspect the navigation response status and URL instead of relying only on the promise rejecting.
Should I set every Puppeteer timeout to zero?
No. Zero disables the timeout for the relevant wait option, which can leave work hanging. Use finite limits and identify which operation needs adjustment.
Where should I look for container-specific launch errors?
Use the official Puppeteer troubleshooting guide and match its environment-specific advice to the actual launch or navigation error.


