ScreenshotNeo

BlogHow-to

How to Read Browser Console Messages in Puppeteer

Capture and inspect browser console messages in Puppeteer with page.on('console'), then trace warnings and errors to their source.

By the ScreenshotNeo team4 October 20268 min read

Use Puppeteer’s console event on the Page and forward each message to Node.js. The event callback receives a ConsoleMessage: call text() for readable output, type() to identify the console method category, and location() or stackTrace() to trace its source. Browser-side console.log() calls do not automatically print in Node.js.

page.on('console', msg => {
  console.log(`[${msg.type()}] ${msg.text()}`);
});

Register the listener before navigation or before the interaction that might produce the message. This guide shows a complete runnable setup, useful message fields, filtering, error boundaries, troubleshooting, and a screenshot option when visual output is the thing you need to inspect.

1. Install Puppeteer and capture console messages

For a new project, install Puppeteer with npm. The package downloads a compatible browser by default; projects using a separately managed browser should consult Puppeteer’s installation and launch configuration for their setup.

npm install puppeteer

Save this as console.js in a project configured for ES modules (for example, with "type": "module" in package.json), then run node console.js:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();

  // Attach before navigation so messages emitted during page load are captured.
  page.on('console', msg => {
    console.log(`[${msg.type()}] ${msg.text()}`);
    console.log('source:', msg.location());
  });

  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
  await page.evaluate(() => {
    console.log('page log');
    console.warn('page warning');
    console.error('page error');
  });
} finally {
  await browser.close();
}

The console.log() calls inside page.evaluate() run in the browser page. The listener callback and its console.log() calls run in Node.js, which is why the event handler is needed to forward page messages.

For CommonJS projects, use const puppeteer = require('puppeteer'); and put the asynchronous work inside an async function main(), then call main().catch(console.error). The event listener code itself is the same.

2. Read the ConsoleMessage fields

A ConsoleMessage gives you several ways to inspect what the page reported. Start with text and type; add source and argument details when the message needs more investigation.

Method What it provides When to use it
text() Formatted message text as a string. Terminal logs, test output, and simple searches.
type() The console method category, such as log, warn, error, info, debug, table, trace, or assert. Filtering or routing messages by category.
location() The message location. Finding the source associated with a message.
stackTrace() An array of locations on the message stack. Following a message through multiple call frames when available.
args() The array of arguments passed to the console call. Inspecting values separately instead of relying only on formatted text.

The exact representation of individual remote objects returned through args() can depend on the Puppeteer version and object. Check the API for the version installed in your project before depending on a particular serialization shape.

3. Filter warnings and errors

Use type() to keep routine logs out of a focused diagnostic stream. This example prints warnings and errors with their source location:

page.on('console', msg => {
  const type = msg.type();
  if (type === 'error' || type === 'warn') {
    console.log(`[${type}] ${msg.text()}`);
    console.log('location:', msg.location());
  }
});

You can preserve other categories in a test artifact or route them to different log levels. The category identifies the console method type; it should not be treated as a complete severity model for every browser problem.

4. Inspect arguments and stack locations

When formatted text is insufficient, print the argument list and stack locations. The following is useful during local debugging; review what values the page may log before retaining it in production, since messages can include sensitive data.

page.on('console', msg => {
  console.log('type:', msg.type());
  console.log('text:', msg.text());
  console.log('args:', msg.args());
  console.log('location:', msg.location());
  console.log('stack:', msg.stackTrace());
});

For durable structured logs, convert the fields your application needs into a deliberate record. Do not assume every console call has a useful source location or that every argument can be serialized directly as ordinary JSON.

5. Understand what the console event does and does not cover

Puppeteer documents the page console event for JavaScript console API calls; its event reference also describes emissions when the page throws an error or warning. It is a view of page console output, not a universal report of every failure in a browser session.

  • Page JavaScript output: listen for console and inspect its ConsoleMessage.
  • Page exceptions: use the page’s error event as a separate signal when tracking uncaught page errors.
  • Failed requests and response status: inspect Puppeteer’s request and response events; a network failure may not appear as a console message.
  • Node.js failures: handle exceptions and promise rejections in the Node process. Browser page events do not replace Node-side error handling.
  • Browser or protocol behavior: use DevTools, Puppeteer protocol logging, or browser process output when the problem is at that layer.

A missing console event does not establish that nothing failed. Choose the diagnostic channel that matches the layer where the failure occurs.

6. Troubleshooting

Symptom Likely cause Fix
Page console.log() output is absent from the terminal. Browser console calls do not directly print to Node.js. Attach page.on('console', ...) and log from the Node-side handler.
Messages from initial page load are missing. The listener was registered after navigation or after the message was emitted. Register the listener immediately after creating the page and before goto().
Warnings or errors are missing from a filtered logger. The filter may be checking the wrong type or forwarding only selected categories. Temporarily log msg.type() and msg.text() for every console event, then adjust the filter.
A failed image, script, or API request has no matching console message. Console events are not a complete network diagnostics stream. Listen to request and response events, and inspect the browser’s network tools for the failing resource.
Source location is empty or not useful. Some message sources do not provide a practical page location or stack. Check stackTrace(), reproduce with DevTools, and add explicit contextual logs in page code if you control it.
Logging arguments produces unfamiliar objects. Console arguments may be represented as remote objects rather than plain Node values. Inspect the API for the installed Puppeteer version; use text() for straightforward display or deliberately extract serializable values in page code.
Browser closes before the listener output is useful. The script may close the browser immediately after starting asynchronous work. Await navigation and page actions that generate the messages, and close the browser in a finally block afterward.

For deeper debugging, Puppeteer’s official guide describes running a visible browser, adding slowMo to observe actions, using DevTools and a browser-side debugger, attaching Node’s inspector for server-side code, logging protocol traffic with NODE_DEBUG, and forwarding browser process logs with dumpio. Protocol logs can contain sensitive information, so handle them accordingly.

7. Reliability, performance, and cost considerations

  • Attach listeners early: install the handler before the action of interest, especially navigation, to avoid missing transient startup messages.
  • Keep handlers lightweight: avoid slow synchronous work in the event callback. For high-volume output, filter or batch records and ensure any asynchronous persistence has an explicit error path.
  • Bound retained logs: a long-running process can accumulate substantial output. Set a retention policy in your own logger and avoid retaining sensitive page data unnecessarily.
  • Use the right signal: console messages are useful for page diagnostics but do not replace request monitoring, exception handling, or visual inspection.
  • Budget browser work: a Puppeteer browser consumes compute and memory while it is running. Reuse a browser process where appropriate, close pages and browsers when finished, and account for navigation timeouts and cleanup in automation jobs.
  • Cost: Puppeteer is open-source software, but running its browser still uses your machine or hosted compute. Actual infrastructure cost depends on where and how often you run it; this guide makes no benchmark or price claim.

8. Or skip the browser setup

If the goal is to inspect a page visually rather than capture its console output, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. It does not expose Puppeteer console messages, so use Puppeteer when you need those diagnostics.

With Puppeteer, a screenshot can be saved after navigation:

await page.goto('https://stripe.com', { waitUntil: 'domcontentloaded' });
await page.screenshot({ path: 'shot.png', fullPage: true });

Or make one API request. See the ScreenshotNeo API documentation for options and setup.

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}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server offers take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan.

Sign up for 1,000 free screenshots a month, with no card required.

9. FAQ

Does Puppeteer capture console messages from every frame?

The listener is attached to a particular Page. For complex pages with frames, verify which page or frame generated the message and instrument the relevant context for your Puppeteer version.

Can I use console events in automated tests?

Yes. Collect messages during the test and assert on the categories or text your test cares about. Keep assertions specific so unrelated page logging does not make the test brittle.

What is the shortest useful handler?

page.on('console', msg => console.log(msg.text())) forwards readable text. Add type() and location() when you need classification or source context.

Where should I check exact API behavior?

Consult the official Puppeteer API reference matching the version installed in your project; documentation pages can describe different package versions.

Official Puppeteer references