ScreenshotNeo

BlogHow-to

How to fix BackstopJS tests failing after a Puppeteer update

Diagnose missing browsers, Chrome launch failures, navigation errors, and screenshot diffs after a Puppeteer update, with fixes for local and CI environments.

By the ScreenshotNeo team4 October 20268 min read

When BackstopJS starts failing after a Puppeteer update, first identify which stage fails: browser installation, browser launch, page navigation, or screenshot comparison. The fix depends on that stage. Puppeteer updates can also change the bundled browser, so a test that launches successfully may still render differently.

Start with the exact error, the installed BackstopJS and Puppeteer versions, the lockfile, Node.js version, operating system or CI image, and whether the project uses puppeteer or puppeteer-core. BackstopJS supports a Puppeteer engine and accepts launch configuration through engineOptions. See the BackstopJS documentation.

1. Classify the failure before changing versions

What you see Likely stage First check
“Could not find Chrome” or a missing executable path Browser installation or cache Did Puppeteer’s install script run? Is the expected browser cache available?
Browser process exits, crashes, or reports a shared library error Launch or host environment Linux dependencies, permissions, writable paths, sandbox requirements, and container image
Navigation timeout, navigation error, or blank page Page loading URL reachability, application readiness, network conditions, and wait configuration
Backstop completes but reports image differences Rendering or comparison Browser build, OS, fonts, viewport, device scale, and application state

Record the full error and whether it occurs locally, in CI, or in both. Avoid changing several variables at once: doing so makes it harder to identify the cause.

2. Check the Puppeteer and browser versions

Puppeteer versions are paired with specific browser versions. Its maintainers explain: “Every Puppeteer release is tightly bundled with a specific browser release to ensure compatibility with the implementation of the underlying protocols, the Chrome DevTools Protocol and WebDriver BiDi.” Check the supported browser table for the version in your dependency tree.

  1. Inspect package.json and the lockfile for BackstopJS, Puppeteer, and any puppeteer-core dependency.
  2. Confirm which Puppeteer copy BackstopJS resolves at runtime; a transitive dependency can differ from the version you expected.
  3. Compare that Puppeteer version with the browser version actually installed or configured.
  4. If local and CI runs use different browser builds, align them before investigating screenshot diffs.

Prefer the browser supplied for the Puppeteer release when your project can download and cache it. If you deliberately manage Chrome or Chromium separately, use the supported-browser table to choose a compatible build and configure its executable path through the engine’s supported options. Puppeteer’s installation guide distinguishes the standard puppeteer package, which downloads a browser, from puppeteer-core, intended for projects that manage their browser themselves.

3. Fix a missing browser download

A missing Chrome binary often means the package installation did not download it, not that a Backstop scenario is invalid. Some package-manager settings suppress dependency install scripts, and restricted build environments may not preserve the browser cache.

  1. Check your package manager’s install-script policy and build logs to confirm Puppeteer’s browser installation step ran.
  2. Check the cache location used during installation and whether the same location is available when BackstopJS runs.
  3. Install the browser explicitly when appropriate:
npx puppeteer browsers install

Puppeteer documents PUPPETEER_CACHE_DIR for configuring the browser cache directory. Set it consistently for install and test steps if your CI jobs use separate stages or users. See the Puppeteer troubleshooting guide for cache and installation details.

4. Fix Chrome launch failures in BackstopJS

BackstopJS documents engineOptions as the place to add Puppeteer options or override defaults. Keep the configuration in the Backstop config file your project actually uses, and verify the expected engine is selected.

// backstop.config.js — example shape; merge into your existing config
module.exports = {
  engine: "puppeteer",
  engineOptions: {
    args: []
  },
  // Keep your existing scenarios, paths, and other settings here.
};

This is a configuration shape, not a universal replacement for an existing Backstop config. Preserve the project’s other settings and add only options justified by the error and environment. In particular, do not copy --no-sandbox from an unrelated answer as a default. If the host requires a sandbox setting, establish that from the CI/container setup and apply it deliberately.

Check the host and container

  • Linux libraries: inspect the launch output for missing shared libraries and install the dependencies required by the browser in the image.
  • Permissions: ensure the process can execute the browser and write its profile, temporary files, and cache.
  • Restricted containers: use writable cache and profile locations available to the job’s user; verify container restrictions against Puppeteer’s troubleshooting guidance.
  • Alpine: check the specific compatibility requirements for the Alpine image and browser build; do not assume a Debian-based fix applies.
  • Executable path: if using a separately managed browser, verify the path exists inside the test environment, not just on the host machine.

5. Diagnose navigation failures separately

If Chrome launches but Backstop reports a navigation timeout, navigation error, or blank capture, the browser installation may already be correct. Check whether the URL is reachable from the same CI container, whether the application server is ready before capture, and whether the page needs a specific element or delay before it is visually stable.

  • Read the first navigation error in the logs; later timeout messages may only be consequences.
  • Check DNS, TLS, proxy, authentication, and network access from the test runner.
  • Wait for the application’s actual ready state instead of assuming that initial document navigation means client rendering is complete.
  • For intermittent failures, compare runs against the same target state and inspect whether the application or network is slow or variable.

Do not treat a longer timeout as the fix for an unreachable page, a crashed browser, or a missing browser binary.

6. Handle screenshot differences after a successful run

A passing launch with changed snapshots calls for rendering diagnosis, not automatically a Puppeteer rollback. A Puppeteer update can bring a different browser build, and visual output can also vary with the host environment and page state. This is an inference from BackstopJS’s visual comparison role and Puppeteer’s version-coupled browser releases; it does not establish that every changed image is caused by Puppeteer.

  1. Confirm local and CI use the same Puppeteer and browser versions.
  2. Keep the operating system or container image, installed fonts, viewport, and device scale consistent.
  3. Confirm the same URL, data, authentication, and application readiness state were captured.
  4. Inspect the diff and decide whether it represents a real application change or environment drift.
  5. Update reference images only after identifying and accepting the rendering change.

7. Make local and CI runs reproducible

  • Commit the lockfile and use the package manager’s lockfile-respecting install mode in CI.
  • Pin the runtime environment, including Node.js and the browser source or build strategy.
  • Install the browser in a build stage that makes its cache available to the test stage, or install it explicitly in the test stage.
  • Use consistent writable cache and profile paths for the CI user.
  • When a failure starts after an upgrade, compare against the last known working dependency and environment state, changing one factor at a time.

Pinning can make the environment easier to reproduce, but an exact version pin cannot be recommended without your current BackstopJS and Puppeteer versions, error output, and operating system or CI image.

8. Troubleshooting common errors

Symptom Likely cause Fix
Could not find Chrome / executable missing Browser download was skipped, cache is absent, or configured path is wrong Check install-script policy and cache; run npx puppeteer browsers install where suitable; verify any custom executable path.
Browser launches locally but not in CI Different OS image, missing libraries, restricted permissions, or unavailable cache Inspect the CI image and user permissions, install required browser dependencies, and make cache/profile paths writable.
Browser exits with a shared library error Required system library is missing from the image Use the error to identify and install the missing dependency in the runtime image.
Chrome starts then crashes in a container Container restrictions, sandbox compatibility, or unwritable profile/temp location Check the environment’s restrictions and writable paths; only change sandbox-related launch arguments when the specific environment requires it.
Timeout waiting for navigation Target is unreachable, app is not ready, or network/page load is slow Check reachability and application readiness from the runner; configure an appropriate wait condition after establishing the cause.
All scenarios show image diffs after update Browser build, fonts, viewport, OS, or page state changed Align capture environments and inspect representative diffs before approving new references.
Local and CI resolve different Puppeteer versions Different lockfile/install state or a transitive dependency mismatch Use the committed lockfile consistently and inspect the resolved dependency tree in both environments.

9. Should you switch BackstopJS engines?

BackstopJS also documents a Playwright engine. Treat an engine change as a deliberate migration: its README says to switch the engine and the corresponding onBefore/onReady scripts together. It is not the first fix for one broken Puppeteer update. First establish whether the issue is browser installation, launch environment, navigation, or expected rendering drift. See the BackstopJS README and engine documentation.

10. Or skip the browser setup

If your task is to capture a page rather than maintain a visual regression suite, ScreenshotNeo is a website screenshot API and MCP server: one GET request returns a PNG, JPEG, WebP, or PDF. This does not replace BackstopJS reference management and visual diffing; it removes the browser setup from standalone capture workflows.

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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));

See the ScreenshotNeo API documentation for parameters and response details. Cookie banners, newsletter popups, and chat widgets are removed before capture; each step can be disabled. Bot checks, blank pages, and failed loads are never billed, and response headers identify the page verdict and billing status. An MCP server exposes take_screenshot, get_page_info, and capture_pdf for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots, and every feature is on every plan. Sign up for 1,000 free screenshots a month, with no card.

FAQ

Is Puppeteer itself part of BackstopJS?

BackstopJS supports a Puppeteer engine, but the browser and Puppeteer versions resolved by your project depend on its dependency and installation setup. Inspect the installed dependency tree and configuration rather than assuming a particular transitive version.

Can I use an external Chrome installation?

Yes, if it is compatible with your Puppeteer version and the executable is available at the configured path in the runtime environment. Check Puppeteer’s supported-browser table before choosing or pinning an external build.

Should I update all screenshot references after upgrading?

Only after confirming that the new render is expected and consistent across the environment you intend to use. A broad diff can indicate environment drift as well as a real change.

What details are needed to identify the exact fix?

Share the full error, BackstopJS and Puppeteer versions, Node.js version, operating system or CI image, browser installation method, and relevant engine configuration. Without those details, a single exact pin or launch patch would be guesswork.