ScreenshotNeo

BlogHow-to

BackstopJS Test Fails Because Chrome Cannot Launch: How to Fix It

Diagnose BackstopJS Chrome launch failures by error message, then fix missing browser downloads, Linux libraries, sandbox settings, and container paths.

By the ScreenshotNeo team4 October 20267 min read

When a BackstopJS test reports Failed to launch chrome!, first read the complete Chrome stderr output and identify the environment running the test. The message is a symptom, not a diagnosis. Check whether the browser is installed and its configured path exists; then check Linux libraries, sandbox permissions, and writable runtime directories. BackstopJS uses Puppeteer for its Chrome headless engine, so the right fix depends on Puppeteer’s browser setup and the container or CI runtime.

Use this order: identify versions and engine, verify the browser binary, inspect its dependencies, check sandbox and user, then check writable paths. Treat a URL that cannot be reached after Chrome launches as a separate application-connectivity issue.

1. Identify the engine, versions, and exact error

Before changing flags, record the full error output, including lines before and after Failed to launch chrome!. Also note whether the test runs locally, in CI, or in Docker, and which user runs the process. Check the project’s BackstopJS and Puppeteer versions and its engine configuration. Examples from older configurations may not match the versions in your project.

npm ls backstopjs puppeteer
npx backstop --version

BackstopJS documentation’s --no-sandbox example specifically refers to configurations generated before version 3.5. It is not a general requirement for current configurations. See the BackstopJS project documentation and compare the example with your installed version.

2. Fix “Could not find Chrome (ver. …)” and “spawn … ENOENT”

These errors usually mean the browser is missing from the runtime or the configured executable path does not exist there. Puppeteer normally downloads a compatible Chrome for Testing during installation. Package managers or build settings that block install scripts can prevent that download. A browser installed on a developer’s laptop is not automatically available in the CI runner or container.

Install the browser in the environment that runs BackstopJS

npx puppeteer browsers install

Run this as part of the environment setup when the install script was skipped. Alternatively, configure the package manager to allow Puppeteer’s installation script, then rebuild the environment. Confirm that the browser download is available to the same user and runtime that launches BackstopJS. Consult Puppeteer installation guidance for supported setup details.

Check a configured executable path

If you use a separately installed Chrome or Chromium, inspect the path from inside the runner or container, not just on the host:

command -v chromium || command -v chromium-browser || command -v google-chrome
ls -l /path/to/configured/chrome

Replace /path/to/configured/chrome with the actual configured path. Correct it or install the browser into the image if it does not exist. If your BackstopJS engine configuration supports an explicit executable path, set it to the binary that exists in that runtime. Puppeteer guarantees compatibility with the browser it downloads; with a custom browser path, version compatibility is your responsibility.

3. Fix missing Linux libraries

If Chrome exists but exits immediately, it may be missing shared-library dependencies or fonts. Inspect the binary on the target machine:

ldd /path/to/chrome | grep not

Use the actual Chrome or Chromium binary path. Install the missing libraries and fonts using packages appropriate for the Linux distribution and browser build, then inspect the dependencies again. Avoid copying a dependency list for a different distribution or release. Puppeteer’s troubleshooting guide describes common Linux dependency issues and points to current Chromium package information.

4. Diagnose sandbox and root-user errors

If the output says Running as root without --no-sandbox is not supported, check which user starts Chrome and how the container is configured. Prefer running Chrome as a non-root user with the sandbox and required container permissions. The official Puppeteer Docker image runs Chrome in sandbox mode and requires the SYS_ADMIN capability; its Docker guide also describes using an init process to manage browser child processes. See Puppeteer’s Docker guide.

For the constrained Docker scenario documented by BackstopJS for older configurations, the engine option is:

{
  "engineOptions": {
    "args": ["--no-sandbox"]
  }
}

Use this only when the error and execution environment call for it. It disables Chrome’s sandbox protections. Do not add it reflexively to every configuration; check the BackstopJS version guidance and container setup first.

5. Make Chrome’s runtime paths writable

Chrome writes profile, cache, and configuration data when it starts. Read-only containers and restrictive CI workspaces can prevent startup even when the browser and its libraries are present. Puppeteer identifies chrome_crashpad_handler: --database is required as one possible symptom of unwritable paths.

Set XDG configuration and cache paths, and Puppeteer’s user-data directory, to writable locations such as /tmp, or mount writable directories owned by the browser process. For example, in a shell-based container entry point:

export XDG_CONFIG_HOME=/tmp/chrome-config
export XDG_CACHE_HOME=/tmp/chrome-cache
mkdir -p "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME"

If your setup specifies a Puppeteer user-data directory, point it to a writable location too. Keep these directories writable by the same user that launches Chrome.

6. Separate browser launch from Docker URL connectivity

Once Chrome starts, a test may still fail because its target page is unreachable. In Docker, localhost inside the container refers to that container, not automatically to the host machine. BackstopJS notes that host.docker.internal can be used in applicable Mac and Windows setups. Check that the test URL resolves and is reachable from the container before treating a page-load failure as a browser launch problem.

Error-to-cause reference

Error clue Likely cause What to do
Could not find Chrome (ver. ...) Puppeteer’s browser download was skipped, or its cache differs in CI. Run npx puppeteer browsers install or allow the install script. Confirm the browser exists in the executing environment.
spawn ... ENOENT The configured executable path does not exist in the runtime. Install a browser in the image or correct the configured path.
Missing .so or ldd ... not found A Linux shared library is absent. Install the distribution-appropriate dependency and rerun ldd.
Running as root without --no-sandbox Chrome is running as root without a compatible sandbox setup. Prefer non-root sandboxed execution with required container permissions. Consider the documented flag only for the matching constrained case.
chrome_crashpad_handler: --database is required Chrome cannot write a profile, configuration, or cache path. Provide writable XDG and user-data directories or writable mounts.
Chrome starts but the page fails to load The target URL may be unavailable from the container. Check container DNS and routing; remember container localhost is not the host.

Choose a browser setup that fits the runtime

Approach Good fit when Trade-off
Puppeteer-downloaded browser You want the browser version Puppeteer expects and can run its install step. The install or browser cache must be available in every execution environment.
Externally managed Chrome or Chromium Your image or platform manages browser packages and you need control of the binary path. You must manage the path and browser compatibility yourself.
Non-root sandboxed container You can configure the user and container permissions. Requires correct ownership and sandbox-capable runtime configuration.
--no-sandbox A constrained environment requires it and the matching launch error occurs. Disables Chrome sandbox protections; avoid using it as a blanket fix.

Performance, reliability, and cost considerations

  • CI reliability: install or provision the browser in the same image or job that runs BackstopJS. Avoid relying on a browser cache that exists only on a developer machine.
  • Version control: record the BackstopJS and Puppeteer versions and use a consistent browser source across environments. Custom browser binaries require compatibility checks.
  • Container lifecycle: writable profile and cache locations, correct ownership, and an init process help avoid startup and child-process management problems.
  • Performance: no launch-time benchmark is established here. Browser download, container startup, and page readiness are separate costs; diagnose the launch failure before tuning page waits.
  • Cost: the cited project guidance provides no supported failure-rate or cost benchmark. For CI, account for the browser installation and runtime resources in your existing job setup rather than assuming a particular savings.

Or skip the browser setup

If you need a screenshot without maintaining a local Chrome installation, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF. Its capture flow accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in headers. An MCP server gives AI agents tools for screenshots, page information, and PDF capture.

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

Replace YOUR_API_KEY with your key. ScreenshotNeo includes 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan. Sign up for free and get 1,000 screenshots a month with no card.

Frequently asked questions

Does “Failed to launch chrome!” tell me what is wrong?

No. Read the complete stderr output; the specific message distinguishes a missing binary, missing library, sandbox issue, or unwritable path.

Should I always add --no-sandbox?

No. Use it only for the documented constrained case when the error and runtime match. A non-root sandboxed setup is preferable when available.

Can a host-installed Chrome fix a CI failure?

Only if that browser is installed and reachable inside the CI environment where BackstopJS runs.

What if Chrome launches but BackstopJS cannot reach my app?

Check the target URL from inside the runner or container. Container networking and host access are separate from Chrome startup.