ScreenshotNeo

BlogHow-to

How to Enable Verbose Puppeteer Logging in the Console

Enable Puppeteer’s internal debug logs, page console output, browser stderr, and protocol diagnostics with the right Node.js switches.

By the ScreenshotNeo team1 October 20267 min read

Use Node’s NODE_DEBUG environment variable before starting your script:

env NODE_DEBUG="puppeteer:*" node script.js

This enables Puppeteer’s documented internal debug output, including protocol-oriented diagnostics. The variable must be present when the Node.js process starts. If you need browser installation logs, use the separate puppeteer:browsers:* namespace.

Verbose Puppeteer logging is different from messages written by the page and from Chromium’s own stdout and stderr. Choose the stream that matches the failure.

Which Puppeteer logging method should you use?

What you need to inspect Method What it shows
Puppeteer internals and DevTools Protocol traffic NODE_DEBUG="puppeteer:*" Internal debug traffic emitted through Node’s util.debuglog
Page JavaScript console calls page.on('console', ...) console.log, console.warn, and related messages generated inside the page
Browser process output dumpio: true Chromium stdout and stderr forwarded to your Node process
Stuck asynchronous protocol calls browser.debugInfo.pendingProtocolErrors Pending protocol error objects and stack traces

These channels are complementary. Start with NODE_DEBUG="puppeteer:*" when you mean “verbose Puppeteer logging.” Add the other channels when the evidence points to page code or the browser process.

Enable internal Puppeteer debug output

macOS and Linux

env NODE_DEBUG="puppeteer:*" node script.js

You can also set the variable for the current shell session:

export NODE_DEBUG="puppeteer:*"
node script.js

Windows PowerShell

$env:NODE_DEBUG = 'puppeteer:*'
node script.js

Windows Command Prompt

set NODE_DEBUG=puppeteer:*
node script.js

Enable it from an npm script

On macOS and Linux, add the environment variable before the command:

NODE_DEBUG="puppeteer:*" node src/capture.js

For cross-platform npm scripts, use a tool such as cross-env in your project, then run:

cross-env NODE_DEBUG=puppeteer:* node src/capture.js

Keep the setting scoped to a debugging command when possible. Verbose output can be large and may include request or session data.

Complete Node.js example

Save this as script.js:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: true,
    dumpio: false
  });

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

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

    page.on('pageerror', error => {
      console.error('[pageerror]', error);
    });

    page.on('requestfailed', request => {
      console.error('[requestfailed]', request.url(), request.failure());
    });

    await page.goto('https://example.com', {
      waitUntil: 'networkidle2',
      timeout: 30000
    });

    console.log('title:', await page.title());
    console.log('pending protocol errors:', browser.debugInfo.pendingProtocolErrors);
  } finally {
    await browser.close();
  }
})();

Run it with internal logging enabled:

env NODE_DEBUG="puppeteer:*" node script.js

The script captures four useful classes of evidence: Puppeteer’s own debug stream, page console events, uncaught page errors, and failed network requests.

Capture messages from the page console

Browser JavaScript runs in a different process context from Node.js. A page’s console.log() does not automatically print in your terminal. Subscribe to the page’s console event:

page.on('console', message => {
  console.log('PAGE LOG:', message.text());
});

You can inspect the message type and location as well:

page.on('console', message => {
  const location = message.location();
  console.log({
    type: message.type(),
    text: message.text(),
    location
  });
});

Use page.on('pageerror') for uncaught exceptions and page.on('requestfailed') for network failures. These events answer different questions from Puppeteer’s internal debug namespace.

Forward Chromium stdout and stderr with dumpio

Set dumpio: true in the launch options when Chromium itself is failing to start, crashing, or writing useful diagnostics:

const browser = await puppeteer.launch({
  dumpio: true
});

dumpio forwards the browser process’s stdout and stderr to the Node.js process. It does not replace NODE_DEBUG="puppeteer:*" and it does not capture page console.log events; use the page event listener for those.

Inspect pending protocol errors

When an asynchronous operation appears stuck, inspect Puppeteer’s pending protocol diagnostics:

console.dir(browser.debugInfo.pendingProtocolErrors, { depth: null });

Run this near a timeout or in a diagnostic cleanup path. The property exposes pending protocol error objects and stack traces, which can reveal where an unresolved command originated.

Log browser installation and launcher activity

Operations performed by @puppeteer/browsers use a more specific namespace:

env NODE_DEBUG="puppeteer:browsers:*" npx @puppeteer/browsers install chrome@stable

The documented channels cover browser cache, file utilities, installation, and launcher activity. Use this namespace when the problem is downloading, locating, or launching a managed browser rather than navigating a page.

Logger and log-level configuration

Puppeteer’s launch and connect options expose a custom logger. The ConnectOptions logger receives a debug channel prefix, is marked experimental, and the API documentation says this usage works only for Chrome in Node.js. The Logger and LoggerFunction API references are also experimental, so check the documentation for the Puppeteer version in your project before relying on this hook.

The global Puppeteer configuration has a logLevel setting with silent, error, and warn values; warn is documented as the default. This controls those listed log levels. It is separate from the debugging-guide command for verbose protocol traffic.

Practical diagnostic workflow

  1. Reproduce the problem with env NODE_DEBUG="puppeteer:*" node script.js.
  2. If the page is producing unexpected output, add a page.on('console') listener.
  3. If Chromium fails to launch or exits unexpectedly, retry with dumpio: true.
  4. If an operation hangs, print browser.debugInfo.pendingProtocolErrors near the timeout.
  5. If the issue concerns browser installation, use puppeteer:browsers:*.
  6. Reduce the output after identifying the failing phase, then reproduce with the smallest useful logging set.

Troubleshooting common errors

No verbose output appears

Cause: The variable was set after Node started, misspelled, or applied to a different shell process.

Fix: Put it directly before the command, for example env NODE_DEBUG="puppeteer:*" node script.js. In PowerShell, assign $env:NODE_DEBUG in the same session.

Page console.log messages are missing

Cause: NODE_DEBUG reports Puppeteer internals, not page JavaScript console calls.

Fix: Register page.on('console', message => ...) before navigation.

Chromium errors are missing

Cause: Browser-process output is separate from Puppeteer’s debug namespace.

Fix: Launch with dumpio: true and capture the Node process’s stdout and stderr.

The terminal is overwhelmed by logs

Cause: The wildcard namespace enables every documented Puppeteer debug channel.

Fix: Reproduce briefly, redirect output to a file, or use a narrower diagnostic path such as page events or dumpio. Do not leave verbose logging enabled in normal production traffic.

Logs contain secrets

Cause: Protocol and request diagnostics can include URLs, headers, cookies, tokens, or other session data.

Fix: Treat captured logs as sensitive, redact them before sharing, and store them with the same controls as application logs.

Browser installation debugging shows nothing

Cause: You enabled puppeteer:* but the operation is running through @puppeteer/browsers.

Fix: Use env NODE_DEBUG="puppeteer:browsers:*" with the install or launcher command.

Performance, reliability, and cost notes

  • Performance: Verbose logging increases terminal or file I/O and can make large navigation runs harder to inspect. Enable it for a bounded reproduction rather than every production request.
  • Reliability: Logging does not change page timing guarantees. Keep explicit navigation and operation timeouts so a diagnostic run cannot wait forever.
  • Reproducibility: Record the Puppeteer version, Node.js version, launch options, URL, and environment namespace alongside the log. Logger APIs marked experimental can change between versions.
  • Security: Review logs before sending them to a ticket or paste service. Puppeteer’s debugging guidance warns that verbose protocol logs may contain sensitive information.
  • Cost: The environment variable itself has no service charge. The practical costs are local CPU, disk, and log-storage usage, especially when browser output is redirected for many runs.

Or skip the browser setup

If your goal is a clean screenshot rather than diagnosing a Puppeteer process, ScreenshotNeo provides a website screenshot API and MCP server. It handles the capture service for you while still exposing the result through a simple request. See the ScreenshotNeo API documentation for the complete option list.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

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)

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}`);

Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed, and each response identifies the result with X-Page-Verdict and X-Billed headers. An MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

FAQ

Does NODE_DEBUG enable Chrome’s own logs?

No. It enables Puppeteer’s internal debug namespace. Use dumpio: true to forward Chromium stdout and stderr.

Can I enable logging from inside the script?

Set NODE_DEBUG before launching Node. The documented procedure relies on the process environment being present at startup.

Which namespace is used for browser installation?

Use puppeteer:browsers:* for @puppeteer/browsers cache, file, installation, and launcher activity.

Why are logger options marked experimental?

Puppeteer’s API reference marks the custom logger interfaces as experimental and documents a Chrome-in-Node.js scope for the ConnectOptions logger. Verify the API reference for your installed version.

Should verbose logs be enabled in production?

Usually only for a short, controlled reproduction. They can add I/O and may expose sensitive request or session data.