To capture JavaScript failures in headless Chrome, attach listeners before navigation or the interaction that can fail. In Puppeteer, use page.on('console') for browser console output and page.on('pageerror') for uncaught exceptions. Record crashes and failed network requests separately, because they represent different failure classes.
The signals you need to capture
A reliable error log distinguishes four events:
- Console messages: calls such as
console.error(),console.warn(),console.log()and informational output from page code. - Uncaught page exceptions: exceptions that escape page code and are reported by Puppeteer’s
pageerrorevent. - Page crashes: a renderer or page process crash. This is not the same as a JavaScript exception.
- Failed requests: network operations that fail before receiving an HTTP response. A 404 or 503 is still an HTTP response and therefore is not a Puppeteer
requestfailedevent.
Do not depend on console.error alone. Code can throw an exception without logging anything, while the console stream also contains ordinary warnings and messages.
Puppeteer: capture console output and uncaught exceptions
Install Puppeteer in a Node.js project with npm install puppeteer. The following complete script attaches every relevant listener before opening the target page, writes a structured record for each event, and exits with a non-zero status when an uncaught exception or request failure is found.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({headless: true});
const page = await browser.newPage();
const records = [];
const save = (record) => {
const item = {time: new Date().toISOString(), ...record};
records.push(item);
console.log(JSON.stringify(item));
};
page.on('console', async (msg) => {
const values = [];
for (const arg of msg.args()) {
try {
values.push(await arg.jsonValue());
} catch {
values.push('[unserializable argument]');
}
}
save({
kind: 'console',
type: msg.type(),
text: msg.text(),
location: msg.location(),
arguments: values
});
});
page.on('pageerror', (error) => {
save({
kind: 'pageerror',
name: error?.name,
message: error?.message ?? String(error),
stack: error?.stack
});
});
page.on('error', (error) => {
save({kind: 'page-crash', message: error?.message ?? String(error), stack: error?.stack});
});
page.on('requestfailed', (request) => {
save({
kind: 'requestfailed',
url: request.url(),
method: request.method(),
failure: request.failure()
});
});
try {
await page.goto('https://example.com', {waitUntil: 'networkidle2', timeout: 30000});
// Put the interaction under test here. For example:
// await page.click('#submit');
await page.waitForTimeout(1000);
} catch (error) {
save({kind: 'automation-error', message: error.message, stack: error.stack});
process.exitCode = 1;
} finally {
const errors = records.filter(r => ['pageerror', 'page-crash', 'requestfailed'].includes(r.kind));
console.error(`Captured ${records.length} records; ${errors.length} non-console failures`);
await browser.close();
}
})();
The console event forwards output from the browser context to Node.js; page-side console.* calls do not automatically appear in the Node.js terminal. The pageerror handler is the important companion because an uncaught exception may never call console.error. Preserve the available name, message and stack, but do not assume every Puppeteer version supplies a native Error object with all of those properties.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →#1 Best Overall
Attach listeners before the triggering action
Register handlers before goto, reload, form submission, clicks, or any script evaluation that can emit an event. A listener added after navigation cannot recover an event that has already been emitted. In a test runner, send the records to the runner’s log or artifact directory rather than only printing them.
Filter without losing the original signal
Keep the event class in storage even if your report displays only errors. A useful policy is to retain all console records, highlight type() === 'error' and type() === 'warning', and fail the test only for pageerror, crashes, or a request failure that your application considers fatal. Whether a warning should fail CI is a project decision.
Testing an error deliberately
To verify the capture path, serve a small page that throws after loading:
<script>
console.error('explicit console error');
setTimeout(() => { throw new Error('uncaught demo failure'); }, 50);
</script>
Navigate to that page with the script above. You should see one console record and one pageerror record. If you see only the first, the exception may be caught by application code, the listener may have been attached too late, or the page may have crashed before delivery.
Rank #2
Playwright alternative
If your project already uses Playwright, use its page event API rather than adding Puppeteer solely for diagnostics. The same separation applies: listen for page console messages, uncaught page exceptions, crashes and request failures, then label each record by its source. Keep the handlers in the fixture or setup code so every test page receives them before navigation.
Playwright can also attach to an existing Chromium instance with chromium.connectOverCDP(). CDP attachment is Chromium-only and has significantly lower fidelity than Playwright’s normal protocol connection. Prefer a regular Playwright connection when you control browser startup and need the fullest Playwright behavior; use CDP when attaching to an already-running Chromium is the requirement.
Interactive diagnosis in Chrome DevTools
For a failure you can reproduce manually, open DevTools’ Console panel. Console entries can show stack traces, and the panel can preserve messages across page loads. Severity filters reduce noise; script-URL and execution-context filters help identify which bundle or frame produced a message. Reproduce the same navigation and interaction used by headless Chrome, then compare the preserved console output with your automated records.
DevTools is useful for discovering the first visible symptom, but it does not replace automation listeners in CI. A headless run needs records that can be saved and inspected after the browser has closed.
Recommended Free Tools
Rank #3
Lower-level Chrome DevTools Protocol capture
When a framework does not expose the signal you need, CDP provides lower-level Runtime and Log event surfaces for console API calls and log entries. The older CDP Console domain is deprecated in favor of Runtime or Log. Raw CDP requires you to manage protocol sessions, browser connection details and event serialization yourself, so it is best reserved for tooling that already operates at protocol level.
Common problems and fixes
No browser-side logs appear in Node.js
Page JavaScript runs in a separate browser context. Add page.on('console', ...) before navigation and print msg.type() and msg.text(). A Node.js console.log statement is unrelated to a page-side one.
An exception is visible in DevTools but missing from the report
Confirm that pageerror was installed before the failing navigation or action. Check whether the code catches the exception and logs a message instead; that produces a console event rather than an uncaught page exception. Also check whether the renderer crashed, which is reported through the page error event.
A 404 or 503 is not reported as requestfailed
This is expected. Puppeteer treats an HTTP response as a completed request even when its status is an error. Inspect responses separately if HTTP status is part of your acceptance criteria; reserve requestfailed for transport-level failures such as an operation that cannot receive a response.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsThe stack is incomplete or the error shape varies
Store the original payload and serialize fields defensively. Use optional chaining and a string fallback, as in the script, instead of assuming error.stack always exists. Source maps and minified bundles can still make a browser-provided stack difficult to read; preserve the raw stack for later symbolication.
Listeners miss errors during startup
Create the page, install listeners, and only then call goto or inject application code. If a framework creates pages for you, put the listeners in its page-created hook or test fixture rather than in an individual test after setup.
Reliability and CI practices
- Use an explicit navigation timeout and record automation-level exceptions separately from page-level errors.
- Include the page URL, frame or execution context when your framework exposes it, and an ISO timestamp.
- Write JSON Lines or another append-only format so one malformed value does not hide earlier events.
- Keep console messages and failures in separate fields; an informational message should not be mistaken for an uncaught exception.
- Close the browser in a
finallyblock so logs are flushed even when navigation fails. - Decide in advance which signals fail a build. Many teams fail on uncaught exceptions and crashes, while treating expected blocked requests or selected console warnings as diagnostics.
Or skip the browser setup
If your goal is a dependable screenshot or PDF around a failing page rather than a custom browser-debugging harness, ScreenshotNeo provides a single HTTP request. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server supplies take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients.
Use the API documentation at screenshotneo.com/docs/ for all options. A minimal cURL capture is:
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutecurl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The same request in Python:
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)
And Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo includes full-page lazy-image loading, CSS-selector element capture, device and viewport controls, dark mode, retina scale, PDF settings, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Parameter names used by other screenshot APIs also work. The Free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Create a free ScreenshotNeo account.
Best Value
Frequently Asked Questions
Can a handled exception be captured as a pageerror?
No. pageerror is for an uncaught page exception. If application code catches the exception and calls console.error, capture it through the console event instead.
Should console warnings fail a CI build?
Only if your project defines them as release-blocking. Store warnings with their original type, then apply that policy in the test runner.
Is CDP attachment available for Firefox or WebKit?
The documented Playwright CDP attachment is supported only for Chromium-based browsers.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




