ScreenshotNeo

BlogGuides

Puppeteer FAQ: Common Browser Automation Questions

Get clear answers about Puppeteer setup, browser support, navigation, headless mode, and common launch failures, with runnable examples and fixes.

By the ScreenshotNeo team4 October 20269 min read

Puppeteer is a Node.js library for automating Chrome and Firefox. Install puppeteer when you want Puppeteer to download a compatible browser; use puppeteer-core when you manage the browser yourself or connect to a remote one. A basic script launches a browser, opens a page, navigates to a URL, and captures a screenshot.

This guide answers the common setup, browser support, behavior, and troubleshooting questions. Version-specific details below reflect the official Puppeteer documentation labeled 25.12.0; check the linked docs when using another version.

1. What is Puppeteer, and who maintains it?

Puppeteer is a Node.js browser automation library and reference implementation maintained by the Chrome Browser Automation team. It lets code launch or connect to a browser, create pages, navigate, interact with page content, and inspect results. Its browser automation uses the Chrome DevTools Protocol (CDP) and WebDriver BiDi.

Install the managed package and run a small screenshot script:

npm install puppeteer

// save as screenshot.mjs
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto('https://example.com', { waitUntil: 'networkidle2' });
  await page.screenshot({ path: 'example.png', fullPage: true });
} finally {
  await browser.close();
}

Run it with node screenshot.mjs. Puppeteer launches headless by default. The install package normally downloads a compatible browser as part of installation; install-script restrictions can prevent that download.

See the official getting started guide and installation guide.

2. Should I use puppeteer or puppeteer-core?

Package Choose it when What you configure
puppeteer You want the package to download and select a compatible browser with convenient defaults. Usually just install the package and call puppeteer.launch().
puppeteer-core You manage a local browser or connect to a remote browser yourself. Provide the browser executable path, a supported channel, or a remote browser connection endpoint.

puppeteer-core does not download Chrome. A local managed installation can be launched by specifying its executable path:

npm install puppeteer-core

// save as local-browser.mjs
import puppeteer from 'puppeteer-core';

const browser = await puppeteer.launch({
  executablePath: '/absolute/path/to/chrome',
  headless: true,
});
try {
  const page = await browser.newPage();
  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
  console.log(await page.title());
} finally {
  await browser.close();
}

Replace the example path with the browser executable installed on the target machine. For a remote browser, use puppeteer.connect() with the connection details supplied by that browser environment, then disconnect when finished. Consult the connect API for the current connection options.

3. How do I install Puppeteer, and why can’t it find Chrome?

For the managed package, install Puppeteer using your package manager. Its install process normally downloads Chrome for Testing and chrome-headless-shell. Package managers and deployment environments may block dependency install scripts; then Puppeteer is installed but the browser is missing.

npm install puppeteer
npx puppeteer browsers install

For other package managers, use their corresponding runner:

yarn dlx puppeteer browsers install
pnpm dlx puppeteer browsers install
bun x puppeteer browsers install

Allowing the package’s install script is another option where appropriate for your package manager. Follow that manager’s current configuration guidance and the Puppeteer installation docs.

Browser cache location

Puppeteer stores downloaded browsers in a cache, normally under the user’s home directory. In containers or CI, the install and runtime users may have different home directories, or the cache may not persist between steps. Set PUPPETEER_CACHE_DIR to a directory available to both the installation and runtime processes:

PUPPETEER_CACHE_DIR=/workspace/.cache/puppeteer npm install puppeteer
PUPPETEER_CACHE_DIR=/workspace/.cache/puppeteer node screenshot.mjs

Use the same value for both commands. Alternatively, configure cacheDirectory in a Puppeteer configuration file and reinstall so that the install process reads it. See Puppeteer configuration and the troubleshooting guide.

4. Which browsers and automation protocols does Puppeteer support?

From Puppeteer v23.0.0 onward, Puppeteer supports Chrome and Firefox. The FAQ describes CDP as Chrome’s default protocol and WebDriver BiDi as Firefox’s default; BiDi can also automate both browsers. CDP support for Chrome continues. Protocol support and API behavior can differ, so check the WebDriver BiDi guide before assuming feature parity.

Puppeteer releases are paired with particular browser versions to preserve compatibility with the underlying protocols. The supported-browser table for documentation version 25.12.0 lists Chrome for Testing 154.0.8037.57 and Firefox 156.0.1. These are version-specific mappings, not permanent requirements. Look up the row matching your installed Puppeteer version in the supported browsers table.

5. What Node.js and system requirements should I check?

The Puppeteer 25.12.0 requirements page lists Node.js 22.12 or later, and TypeScript 5.0.1 or later when using TypeScript. Browser support and operating-system libraries depend on the target operating system and architecture. Chrome for Testing requirements cover Windows x64, macOS x64 and arm64, and Debian/Ubuntu or openSUSE/Fedora Linux on x64 and arm64, with required system packages varying by distribution.

Check the system requirements page on the machine or container where the browser will run. A script working on a developer laptop does not establish that the CI image has the required libraries, unpacking utilities, permissions, or browser cache.

6. What does headless mean? Which launch mode should I choose?

Launch setting What it does Useful for
headless: true (default) Runs Chrome without a visible browser window. Most automated jobs and captures where no visible interaction is needed.
headless: 'shell' Uses the separate chrome-headless-shell binary. Automation that does not need the complete Chrome feature set; the shell may be more performant but does not match regular Chrome completely.
headless: false Opens a visible browser window. Local debugging and workflows that require observing the browser.
const browser = await puppeteer.launch({ headless: 'shell' });

Choose the shell only if its behavior differences are acceptable for the task. For visual debugging, use headless: false on a machine with a display. See headless modes.

7. What counts as a navigation?

Puppeteer treats any URL change as navigation. This includes a normal document load, an anchor navigation, or a History API URL change in a single-page application. Navigation waits should reflect what the page is expected to do; an SPA may change its URL without loading a new document.

page.goto() accepts wait conditions such as load, domcontentloaded, networkidle0, and networkidle2. Network-idle conditions can be unsuitable for pages with persistent requests such as polling or analytics. If a specific page element indicates readiness more reliably, wait for that selector after navigation:

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('[data-ready="true"]', { timeout: 15000 });

For navigation-triggering actions, start waiting before the action to avoid missing a fast navigation:

const navigation = page.waitForNavigation({ waitUntil: 'domcontentloaded' });
await page.locator('a.next').click();
await navigation;

8. Are Puppeteer input events trusted?

Puppeteer-generated input actions are trusted browser input events and include the accompanying events expected from user interaction. By contrast, calling a DOM method such as element.click() inside page.evaluate() creates an untrusted event. Sites may treat these differently. This distinction is not a way to bypass site security or automation policies.

// Browser automation input:
await page.locator('button[type="submit"]').click();

// DOM call executed by page JavaScript; produces an untrusted event:
await page.evaluate(() => document.querySelector('button[type="submit"]').click());

Use Puppeteer’s input APIs when the goal is to automate an actual interaction. See the official FAQ for the current explanation.

9. Does Puppeteer support audio and media playback?

Puppeteer controls a browser; it does not make media playback independent of browser and operating-system conditions. Playback can depend on autoplay restrictions, user interaction, codecs, installed libraries, and the runtime environment. If a test needs playback, use the browser’s supported media APIs and verify the specific browser build and host prerequisites. Do not treat a successful page navigation as evidence that audio or video played.

10. How do I troubleshoot common installation and launch errors?

Symptom Likely cause What to check or fix
Could not find Chrome (ver. ...) or no local browser The browser download install script was blocked, the browser was not installed, or the browser cache differs between install and runtime. Run npx puppeteer browsers install; check the package manager’s install-script policy; make PUPPETEER_CACHE_DIR consistent across install and execution.
Failed to launch or shared library errors on Linux Required system packages are missing, or the browser cannot run in the current host environment. Compare the target distribution and architecture with the system requirements; install the listed packages and inspect the full browser stderr output.
Chrome exits immediately in Docker The image may lack Chrome dependencies, the browser cache may not be available, or the container user and permissions may not match the setup. Use a compatible image and user setup, preserve the browser installation, and follow the Docker guide.
Sandbox or permission error The host’s user, kernel, container configuration, or sandbox setup may not allow Chrome to start. Configure a working sandbox for the environment and review the exact host-specific troubleshooting instructions. The docs strongly discourage --no-sandbox; do not make it a routine production fix.
Browser version mismatch or protocol errors The selected browser version may not be paired with the installed Puppeteer release. Check the supported-browser table for that Puppeteer version, then install its compatible browser or update the version pair deliberately.
Works locally but not in CI CI may use a different Node version, operating system, architecture, cache directory, libraries, or user. Compare those environment details with the requirements page; explicitly install and cache the browser in the CI workflow.
Navigation wait times out on a live-updating page Waiting for network idle may never finish while the page keeps connections open or makes recurring requests. Wait for domcontentloaded or a page-specific selector, and set a timeout appropriate to the operation.
Cannot find module under a custom resolver An older Node.js version or custom resolver may not handle the package layout correctly. Use a supported Node.js version and update the resolver or its parent tooling; consult the troubleshooting entry for the exact error.

Capture the full error, Puppeteer version, browser version, Node version, OS, architecture, and whether the process runs in a container. These details make environment-specific failures much easier to diagnose. The official troubleshooting guide is the reference for current fixes.

11. What helps Puppeteer run reliably and efficiently?

  • Pin compatible versions. Keep Puppeteer and its managed browser installation aligned. Check the version mapping when upgrading.
  • Persist the browser cache. In CI, install browsers deliberately and cache the same directory used at runtime.
  • Wait for the right readiness signal. Use a selector or document event when network-idle waits do not describe the page’s real readiness.
  • Always close resources. Close pages or the browser in cleanup paths so failed navigation does not leave processes running.
  • Pick headless mode for the job. Default headless Chrome is the general choice; shell mode trades feature completeness for a potentially faster automation binary.
  • Budget for browser installation and runtime. The documentation’s 25.12.0 installation guide estimates downloads at about 170 MB on macOS, 282 MB on Linux, and 280 MB on Windows. These are documentation estimates, not independent measurements, and can change. Plan for browser disk, memory, startup time, and required host libraries.

For questions, the official FAQ directs users to Stack Overflow for questions and GitHub Issues for bug reports; search existing posts first. Installation and runtime problems should be compared with the troubleshooting guide before opening an issue.

12. Or skip the browser setup

For a screenshot rather than a general browser automation workflow, ScreenshotNeo returns a PNG, JPEG, WebP, or PDF from one API request. It accepts cookie or consent banners like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status.

Example cURL request:

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,
)
r.raise_for_status()
with open("shot.webp", "wb") as f:
    f.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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

ScreenshotNeo also provides an MCP server for AI agents, with take_screenshot, get_page_info, and capture_pdf tools. One thousand screenshots per month are free with no card; paid plans start at $5 for 3,000. Every feature is on every plan. See the ScreenshotNeo API documentation for request options. Sign up for 1,000 free screenshots a month, with no card required.

Frequently asked questions

Can I use Puppeteer with Firefox?

Yes. Puppeteer supports Firefox from v23.0.0 onward. Check the current browser mapping and protocol guide for your installed Puppeteer version.

Does Puppeteer stop supporting CDP now that it supports BiDi?

No. The FAQ says Chrome automation with CDP will continue alongside WebDriver BiDi support.

Where should I ask another Puppeteer question?

Use Stack Overflow for questions and GitHub Issues for bug reports, after searching for an existing answer or report. Include version and environment details for runtime problems.