The short answer
There is no built-in pre-flight compatibility check and no automatic fallback binary in Puppeteer. The reliable strategy is the opposite of probing: pin the browser to the exact build your Puppeteer version expects, let Puppeteer manage it via its cache, and verify the resolved version at runtime with browser.version(). If you must use a custom executablePath, the practical "detection" is a cheap launch probe with dumpio: true so the real Chromium error surfaces instead of a generic timeout.
Why mismatches happen
Each Puppeteer release is tested against a specific Chrome for Testing revision. By default, puppeteer.launch() downloads and uses that pinned build from its cache (typically ~/.cache/puppeteer, overridable via PUPPETEER_CACHE_DIR or a .puppeteerrc file). Mismatches almost always come from one of three sources:
- A custom
executablePath pointing at a system Chrome/Chromium that updates independently.
PUPPETEER_SKIP_DOWNLOAD / PUPPETEER_SKIP_CHROMIUM_DOWNLOAD forcing reliance on whatever binary the environment provides.
- A stale cache after upgrading the Puppeteer package, or a shared CI cache keyed incorrectly.
The failure mode is usually a DevTools protocol error (target closed, WebSocket disconnect) rather than a clean "version mismatch" message, because the protocol evolves between Chrome versions. Matching only the major version is often not enough.
Recommended approach
- Prefer the pinned build. In CI, install the exact browser Puppeteer expects:
npx puppeteer browsers install chrome
Then launch with no executablePath override. This removes the mismatch class entirely.
- Verify at runtime, not before launch. After connecting, log and assert:
const browser = await puppeteer.launch({ dumpio: true });
console.log(await browser.version());
Compare this against the revision listed in your Puppeteer version's release notes. A startup assertion that fails the CI job on drift is cheaper than a pre-launch probing scheme.
- If you must use a system binary, use
channel: 'chrome' (or an explicit executablePath) and treat the first launch as the probe: wrap it in try/catch, and on failure re-run with dumpio: true to capture Chromium's stderr. That output distinguishes a version/protocol failure from sandbox or missing-dependency errors.
On fallback binaries
Puppeteer has no flag to automatically try a second binary when the first fails. If you need fallback behavior, implement it yourself: attempt launch with the pinned cache build first, catch the error, then retry with executablePath pointing at the system binary — and log which path succeeded so drift is visible. Keep the fallback list short and explicit; silently landing on an untested browser version is worse than a loud failure.
Caveats
Option names, cache paths, and supported channels vary across Puppeteer major versions; the above reflects recent releases — check the changelog for your installed version. Also note that in containers, --no-sandbox failures when running as root are frequently mistaken for version mismatches; dumpio: true output separates the two.