ScreenshotNeo

BlogHow-to

How to Fix Puppeteer’s Chrome ENOENT Launch Error

Fix Puppeteer’s Chrome ENOENT error by checking the executable path, browser download, cache, runtime user, and Linux dependencies.

By the ScreenshotNeo team30 September 202610 min read

How to Fix Puppeteer’s Chrome ENOENT Launch Error

Direct answer: Puppeteer’s Chrome ENOENT error means the process tried to spawn a browser executable at a path that does not exist in the environment where your script is running. Check the exact path first, then confirm that Chrome was downloaded, that the install and runtime users can see the same cache, and that the deployment image contains the browser’s Linux dependencies. Do not treat --no-sandbox as a generic ENOENT fix: a missing executable and a sandbox failure are different problems.

The most recognizable form is:

Error: Failed to launch chrome!
spawn /usr/bin/chromium-browser ENOENT

This guide gives a repeatable diagnosis for local development, CI, Docker, serverless deployments, and projects using either puppeteer or puppeteer-core. It also shows how to avoid managing Chrome entirely with ScreenshotNeo after the repair steps.

1. Understand what ENOENT is reporting

ENOENT is the operating system’s “no such file or directory” result. In this context, Node attempted to start the executable supplied to Puppeteer, but that file was unavailable to the process. The path may be wrong, Chrome may never have been installed, or the runtime may be looking in a different filesystem, cache, user home directory, or container image than the installation step.

The error does not by itself prove that Chrome crashed. If the executable exists but cannot start because a shared library is missing, the symptom and remedy are different. If Chrome starts and then reports a sandbox problem, that is also a separate failure.

What the error usually means

Observation Likely cause First action
Path ends in chromium-browser or another missing file Stale or incorrect executable path Check the path inside the actual runtime
puppeteer-core is installed No browser download is expected Provide a real browser path or managed remote browser
Works locally, fails in CI or Docker Different image, user, filesystem, or install stage Inspect the runtime image and cache as the deployment user
Executable exists, launch still fails Missing shared libraries or platform dependencies Run ldd against Chrome and inspect missing entries
Error mentions sandbox Chrome security configuration issue Diagnose sandbox permissions separately

2. Identify your Puppeteer browser-management model

Your first diagnostic question is which package and installation model the project uses.

ENOENT diagnosis follows the executable path from installation into the actual runtime.
ENOENT diagnosis follows the executable path from installation into the actual runtime.

puppeteer

The regular puppeteer package normally downloads a compatible Chrome for Testing browser during installation. If the download succeeds, your launch code can usually use Puppeteer’s managed browser without an explicit executable path.

puppeteer-core

puppeteer-core does not download Chrome. Puppeteer’s installation documentation describes it as a library for driving anything that supports the DevTools protocol and says that it is fully programmatic, with no defaults assumed. You must provide a browser yourself, connect to a remote browser, or use another browser-management system.

Check the dependency:

npm ls puppeteer puppeteer-core

If your application uses puppeteer-core accidentally after a dependency migration, installing the package alone will not put Chrome on disk.

3. Follow the diagnosis in order

Step 1: Print the runtime and configured path

Run these commands in the same shell, container, CI job, service account, or deployment image that launches your application:

node --version
npm ls puppeteer puppeteer-core
command -v google-chrome || true
command -v chromium || true
command -v chromium-browser || true
ls -l /usr/bin/google-chrome /usr/bin/chromium /usr/bin/chromium-browser 2>/dev/null || true
printf 'HOME=%s\n' "$HOME"
printf 'PUPPETEER_CACHE_DIR=%s\n' "$PUPPETEER_CACHE_DIR"

A path that exists on your laptop is irrelevant if it does not exist in the deployment runtime. Likewise, a browser installed during a build stage may be invisible in the final image.

Step 2: Check whether installation scripts were blocked

Package-manager settings can block dependency installation scripts. When that happens, puppeteer may be present while its Chrome for Testing download is absent. The documented manual remedy is:

npx puppeteer browsers install

Run it in the project and environment that will execute the code. If you changed Puppeteer’s browser-download configuration, reinstall the browser after the change. In CI, make browser installation an explicit build step instead of assuming a developer’s local cache will be available.

Step 3: Verify the cache location and runtime user

Puppeteer’s default browser cache is ~/.cache/puppeteer. The tilde is user-specific: installing as one user and running as another can produce an apparent missing browser. The cache can be changed through Puppeteer configuration or the PUPPETEER_CACHE_DIR environment variable.

echo "$HOME/.cache/puppeteer"
find "${PUPPETEER_CACHE_DIR:-$HOME/.cache/puppeteer}" -maxdepth 4 -type f -perm -111 2>/dev/null | head

For a container or multi-stage build, make the cache path deterministic and copy it into the final image, or install the browser again in the final image. Confirm ownership and permissions for the account that starts Node.

Step 4: Use an explicit executable path when Chrome is system-managed

If your operating system or base image installs Chrome or Chromium, pass its actual path:

const puppeteer = require('puppeteer-core');

(async () => {
  const browser = await puppeteer.launch({
    executablePath: process.env.CHROME_BIN || '/usr/bin/google-chrome',
    headless: true
  });
  const page = await browser.newPage();
  await page.goto('https://example.com', {waitUntil: 'networkidle2'});
  console.log(await page.title());
  await browser.close();
})();

Replace the example path with the path found inside the runtime. Do not copy a path from a different distribution or image without checking it. If your project uses a Puppeteer configuration file or environment variables, remember that puppeteer-core does not use Puppeteer’s default configuration behavior; configure the browser through its programmatic launch options.

Step 5: Separate missing files from missing libraries

When the executable exists, inspect its dynamic dependencies on Linux:

ldd /path/to/chrome | grep not

Any unresolved library must be installed for the specific distribution and image. The required package names vary by operating system and browser version, so use the current dependency list for your distribution. Docker images are especially likely to omit libraries that a desktop installation already provides.

4. Working launch examples

Managed Chrome with puppeteer

const puppeteer = require('puppeteer');

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

System Chrome with puppeteer-core

const puppeteer = require('puppeteer-core');

(async () => {
  const executablePath = process.env.CHROME_BIN;
  if (!executablePath) throw new Error('Set CHROME_BIN to a browser executable');

  const browser = await puppeteer.launch({executablePath, 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();
  }
})();

Minimal Docker verification

Do not debug only from the host. Add a temporary diagnostic command to the image or CI job:

docker run --rm your-image sh -lc 'whoami; echo "$HOME"; command -v google-chrome || true; command -v chromium || true; find /root/.cache/puppeteer /home -maxdepth 5 -type f -perm -111 2>/dev/null | head'

This establishes the user, home directory, executable visibility, and cache visibility in one place.

5. CI, Docker, and deployment edge cases

  • Build and runtime images differ: installing Chrome in a builder stage does not make it available in the final stage unless the browser and required libraries are copied or installed again.
  • Different users: a root build may populate /root/.cache/puppeteer, while the service runs as an unprivileged user with another home directory.
  • Read-only filesystems: launch may fail later if Chrome cannot write its profile or temporary files. Give the process a writable temporary location appropriate to your platform.
  • Blocked postinstall scripts: package installation can finish without the browser. Run npx puppeteer browsers install explicitly.
  • Environment-specific paths: macOS, Linux, Windows, and container images place system browsers differently. Discover the path in each target environment.
  • Remote browser services: with puppeteer-core, connecting to a remote DevTools endpoint can be valid; there is no local Chrome binary to find.

Chrome for Testing downloads are large: the installation guide gives approximate sizes of 170 MB on macOS, 282 MB on Linux, and 280 MB on Windows. Account for that storage and network cost in ephemeral CI workers and image layers.

6. Common errors and fixes

Error or symptom Cause Fix
spawn /usr/bin/chromium-browser ENOENT The configured executable is absent at that path Install a browser in the runtime or set executablePath to the real path
puppeteer-core launches with no path The package does not download Chrome Provide a local executable or connect to a managed browser
Works after local npm install, fails in CI Install scripts or browser cache are unavailable in CI Allow the download or run npx puppeteer browsers install in CI
Browser is present in build logs but absent at runtime Cache or browser was left in another stage, user home, or image Align cache paths and copy/install into the final runtime
ldd reports not found System libraries are missing Install the packages required by the target distribution
Sandbox error after the binary is found Security or user configuration, not ENOENT Fix the sandbox and permissions; do not disable it as a universal workaround
Path exists locally but not in production Different operating system or container filesystem Discover and configure the path inside production

7. Reliability and performance practices

  • Install the browser once during image build or an explicit CI setup phase, then verify it before running application tests.
  • Keep the browser cache path stable across installation and runtime. Set PUPPETEER_CACHE_DIR when the default home directory is not stable.
  • Log the package type, runtime user, cache directory, executable path, and browser version at startup. These values turn an opaque launch failure into a diagnosable record.
  • Reuse a browser process for multiple pages when your workload permits it, while closing pages and the browser in finally blocks.
  • Set navigation and operation timeouts deliberately. A launch fix does not solve pages that never finish loading.
  • Keep CI images and production images close enough that browser paths and shared libraries do not drift.
  • Pin and review browser and Puppeteer updates as a pair. Installation requirements and supported platforms can change over time.

8. Or skip the browser setup

If your goal is a reliable website image rather than maintaining Chrome in every runtime, ScreenshotNeo provides a single HTTP request to return a PNG, JPEG, WebP, or PDF. Its capture flow accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off.

A capture service can remove common overlays before returning the image.
A capture service can remove common overlays before returning the image.

Only clean shots are billed. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers. An MCP server also lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf.

See the ScreenshotNeo API documentation for the full request options. The basic call is:

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

ScreenshotNeo also supports full-page captures with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, retina scale, PDF paper sizes and page ranges, HTML/CSS rendering, custom CSS and JavaScript, clicks, selector or network-idle waits, request and resource blocking, custom headers and cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, and an OpenAPI specification.

The Free plan includes 1,000 screenshots each month with no card. Paid plans start at $5 for 3,000 shots; yearly billing gives two months free, and every feature is available on every plan. Create a free ScreenshotNeo account.

9. Puppeteer ENOENT checklist

  1. Read the complete error and copy the exact executable path.
  2. Run command -v and ls -l inside the failing runtime.
  3. Confirm whether the project uses puppeteer or puppeteer-core.
  4. If using puppeteer, verify the browser download and run npx puppeteer browsers install when needed.
  5. Check ~/.cache/puppeteer, PUPPETEER_CACHE_DIR, the runtime user, and multi-stage image boundaries.
  6. Set executablePath only to a path that exists in that runtime.
  7. If the file exists, run ldd chrome | grep not and install missing libraries.
  8. Diagnose sandbox errors independently and avoid using --no-sandbox as an ENOENT remedy.

10. FAQ

Does reinstalling Node fix Chrome ENOENT?

Usually no. ENOENT points to the browser executable or its environment. Reinstalling Node helps only if it also changes a broken dependency installation; inspect the path and browser cache first.

Should I always set executablePath?

No. Managed puppeteer can use its downloaded browser. Set it when Chrome is installed by the operating system, supplied by a container, or managed outside Puppeteer.

Why does puppeteer-core not download Chrome?

That package is designed for programmatic control of an existing or remotely managed DevTools-compatible browser. Supplying the browser is part of the application’s configuration.

Can missing shared libraries produce ENOENT?

The executable path problem and missing-library problem should be distinguished. If the file exists, inspect its dependencies with ldd and resolve the libraries required by the target image.

Will --no-sandbox fix this error?

No. It addresses a sandbox configuration failure, not a missing executable. Keep the browser sandbox enabled unless you have a specific, reviewed reason and an appropriate security configuration.