Puppeteer FAQ: Common Questions and Troubleshooting
Fix Puppeteer install and launch problems, choose the right browser and package, and capture pages reliably with runnable examples.
Puppeteer is a Node.js library for automating Chrome and Firefox. For the most reliable setup, install puppeteer so it downloads its compatible Chrome for Testing, launch the browser, navigate to a page, wait for the content you need, capture it, and close the browser. If Chrome cannot be found, the install script may have been skipped. If Chrome will not launch on Linux, check its shared libraries, sandbox configuration, and writable profile directory before changing launch flags.
This guide covers package choice, browser compatibility, launch failures, page interaction, screenshots, and common environment-specific issues. Puppeteer’s documentation referenced here identifies version 25.12.0; requirements and browser support can change, so check the live documentation when upgrading.
1. What is Puppeteer, and which browsers does it support?
Puppeteer is a Node.js browser automation library. Starting with v23.0.0, it supports Chrome and Firefox. Chrome uses the Chrome DevTools Protocol (CDP) by default; Firefox uses WebDriver BiDi by default. Protocol support can differ, so do not assume every API behaves identically across both browsers. Puppeteer says its releases are paired with specific browser releases to protect compatibility with the automation protocols.
The bundled browser is the compatibility baseline. You can select a system browser or executable path, but Puppeteer does not guarantee that an arbitrary external browser works with every release.
2. Should I install puppeteer or puppeteer-core?
| Package | Browser download | Use it when |
|---|---|---|
puppeteer |
Normally downloads a compatible Chrome for Testing during installation. | You want Puppeteer to manage its standard browser setup. |
puppeteer-core |
Does not download a browser. | Your application manages a local browser or connects to a managed or remote browser. |
For a first local script, puppeteer is generally the simpler choice. Use puppeteer-core when the browser lifecycle belongs to your deployment or remote-browser service; provide the connection or executable configuration your environment requires.
3. What versions and system requirements should I check?
The current Puppeteer 25.12.0 system requirements page lists Node.js 22.12 or later, and TypeScript 5.0.1 or later if you use TypeScript. Verify the live requirements for the Puppeteer release and platform you deploy; do not update a production image based on a requirement from an older release.
Use the browser Puppeteer installed for its release unless you have a reason to manage a different browser. The LaunchOptions API allows a channel or explicit executable path, but the bundled browser is the supported compatibility baseline.
4. Install Puppeteer and take a screenshot
Install the package in a Node.js project:
npm install puppeteer
Save this as screenshot.mjs and run node screenshot.mjs https://example.com. It writes a full-page PNG and closes the browser even if navigation or capture fails.
import puppeteer from 'puppeteer';
const target = process.argv[2] ?? 'https://example.com';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto(target, { waitUntil: 'networkidle2', timeout: 60_000 });
await page.screenshot({ path: 'page.png', fullPage: true });
console.log('Saved page.png');
} finally {
await browser.close();
}
networkidle2 is one possible navigation condition, not a guarantee that every application has finished rendering. Pages with persistent network connections or delayed client-side content may need a selector wait instead. Choose a condition that matches the page state you need.
The official getting-started and screenshot guides show the launch, navigation, capture, and close workflow: getting started and screenshots.
5. How do I wait for and interact with page content?
Prefer Puppeteer’s locator APIs for normal interaction. They provide a higher-level workflow for finding and acting on page elements. Use waitForSelector when you specifically need a lower-level wait for a matching DOM element.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
// Higher-level interaction API: wait for and click a matching element.
await page.locator('a[href="/docs"]').click();
// Lower-level DOM wait: wait until a result element exists.
await page.waitForSelector('[data-testid="results"]', { timeout: 10_000 });
await page.screenshot({ path: 'results.png' });
} finally {
await browser.close();
}
Replace selectors with ones that exist on the target page. If content is inserted asynchronously, waiting for the relevant element is often more dependable than choosing an arbitrary delay. The page interactions guide explains locators and selector waits.
6. Why does Puppeteer say “Could not find Chrome”?
The standard puppeteer package normally downloads Chrome for Testing during installation. If a package manager blocks install scripts, the package can be present while its browser is missing.
- Check whether your package manager skipped Puppeteer’s install script.
- Install Puppeteer’s browser manually using the browser-install command documented in the installation guide, or configure your package manager to permit the required install script according to its policy.
- If your environment supplies its own browser, use
puppeteer-coreand configure its executable path or browser connection.
Since Puppeteer v19, the default browser cache is ~/.cache/puppeteer. Set PUPPETEER_CACHE_DIR to relocate it when the default location is not persistent or writable. Confirm that the install and runtime environments use the same cache location.
Do not treat puppeteer and puppeteer-core as interchangeable here: the latter intentionally does not download Chrome.
7. Why will Chrome not launch on Linux or in a container?
Diagnose the failure in this order so that each change addresses a likely cause:
- Check missing shared libraries. Use
lddon the browser executable to identify unresolved dependencies. Install the packages appropriate to your Linux distribution, consulting the maintained dependency references linked from Puppeteer’s troubleshooting guide. Package names differ by distribution and change over time. - Check sandbox configuration. Chrome’s sandbox protects the host from page content. Configure it for your runtime rather than disabling it by default. Puppeteer strongly discourages
--no-sandbox; use it only when you absolutely trust the content and understand the security tradeoff. - Check writable paths. Puppeteer needs a writable browser profile directory. Ensure the runtime user can write its user-data directory and browser cache. In containers, a non-privileged user and writable profile/cache locations are practical choices where the environment permits.
- Check platform-specific restrictions. The troubleshooting guide notes that Ubuntu 23.10+ AppArmor user-namespace restrictions can interfere with Chrome for Testing; Windows policies may conflict with Puppeteer’s default extension behavior or sandbox permissions; and Alpine does not support Chrome out of the box. These are targeted platform checks, not universal causes.
Changing several flags at once makes the underlying cause harder to identify. Capture the exact launch error, browser version, operating system, and runtime user when diagnosing a deployment.
8. Which launch options should I change?
Set only the options that correspond to your environment. Puppeteer’s LaunchOptions reference documents the current API.
| Option or setting | What it controls | When to consider it |
|---|---|---|
headless |
Whether to launch without a visible browser window. | Use headless mode for server-side capture; choose a different mode only when your workflow needs it. |
browser |
Browser selection. | When choosing a supported browser such as Firefox, verify protocol and API coverage for your use case. |
channel |
A Chrome channel selection. | When you intentionally use an installed Chrome channel instead of the bundled browser. |
executablePath |
Path to a browser executable. | When your deployment manages the browser binary. Compatibility with arbitrary versions is not guaranteed. |
timeout |
Maximum startup time. | When browser startup is slow in the actual deployment environment; first diagnose resource or dependency problems. |
userDataDir |
Browser profile directory. | When a persistent or explicitly located profile is required; ensure it is writable and not concurrently reused unsafely. |
args |
Additional browser command-line arguments. | Only when a specific documented environment requirement calls for them. |
Puppeteer configuration also covers browser choice, download behavior, cache directory, and executable path, with environment-variable overrides. See the configuration API for version-specific names and defaults.
9. How do I capture a page without running my own browser?
For a fully managed screenshot request, ScreenshotNeo is a website screenshot API and MCP server for developers. A GET request returns an image or PDF. Its API documentation lists the available parameters and response behavior.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', bytes));
Use your API key in place of YOUR_API_KEY. The Node.js example checks the HTTP status before saving the response. Read the response headers and API documentation when handling verdicts, billing, or errors in an application.
10. ScreenshotNeo: skip the browser setup
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers identify the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
See the ScreenshotNeo API docs and sign up for 1,000 free screenshots a month, with no card.
11. Troubleshooting quick reference
| Symptom | Likely cause | What to do |
|---|---|---|
| “Could not find Chrome” | Browser download was skipped, cache is missing, or runtime cannot access it. | Run the documented browser install step; check install scripts and cache path/permissions. |
| Browser exits immediately on Linux | Missing shared library, sandbox problem, or restricted runtime. | Inspect dependencies with ldd, then check sandbox and platform-specific guidance. |
| Profile or cache write error | Runtime user lacks write access. | Use a writable userDataDir and cache directory. |
| Selector wait times out | Selector is incorrect, content never appeared, or the page has not reached the needed state. | Inspect the actual DOM and wait for the page-specific element/state; avoid assuming navigation alone means rendering is complete. |
| Screenshot is incomplete | Capture happened before lazy or asynchronous content rendered. | Wait for a relevant selector or appropriate page state before calling screenshot(). |
| External Chrome behaves differently | Browser version is outside Puppeteer’s compatibility baseline. | Return to the bundled browser or verify the external browser’s compatibility with the current release. |
| Firefox API behavior differs | Protocol support differs from Chrome/CDP. | Check current Firefox/WebDriver BiDi support for the API in question. |
12. Performance, reliability, and cost considerations
Puppeteer runs a real browser, so resource use and completion time depend on the page, browser, and deployment. Reuse a browser process for multiple pages when appropriate instead of starting one for every capture, and close pages and browsers when finished. Keep timeouts bounded, wait for the specific content you need, and avoid waiting for network idleness on pages that maintain long-lived connections.
Reliability depends on matching Puppeteer with its compatible browser, shipping required system dependencies, providing writable cache/profile locations, and configuring the sandbox correctly. A system browser or a manually managed browser can simplify a managed deployment but adds responsibility for version compatibility.
The research sources provide no cross-environment performance benchmark or Puppeteer price, so this guide does not claim a numeric speed or cost. Account for your own compute, browser storage, and operational maintenance. ScreenshotNeo offers a separate usage-based plan structure: 1,000 monthly shots free, then paid tiers from $5 for 3,000; its response headers distinguish clean shots from non-billed outcomes.
13. FAQ
Can Puppeteer use Firefox?
Yes. Puppeteer supports Firefox from v23.0.0 onward, using WebDriver BiDi by default. Check protocol support for the API you need.
Does Puppeteer work with any installed Chrome?
It can select a channel or executable path, but the bundled browser is the guaranteed compatibility path.
Is waitForSelector obsolete?
No. It remains available as a lower-level DOM wait; Puppeteer recommends locators as the higher-level interaction API.
Where can I find the current requirements?
Check the official system requirements and the documentation for the exact Puppeteer release you deploy.


