playwright expect.toHaveScreenshot: A snapshot doesn't exist on the first CI run - how to generate
Generates Playwright screenshot baselines so the first CI run stops failing. Use when expect.toHaveScreenshot fails with A snapshot doesn't exist on the first CI run. Covers generating baselines with --update-snapshots in CI-like conditions, committing them, and configuring CI to fail on missing snapshots. Not for snapshot mismatches.
TL;DR
Playwright refuses to compare against a snapshot that does not exist unless you tell it to create baselines. The first CI run therefore fails every screenshot assertion, which is correct behavior: CI should never silently invent baselines. Generate the snapshots once with --update-snapshots on a machine matching CI's OS, browser, and viewport, commit the files to git, and set CI to fail on missing snapshots instead of creating them.
The query
playwright expect.toHaveScreenshot: A snapshot doesn't exist on the first CI run - how to generateUse this when
- expect.toHaveScreenshot fails with A snapshot doesn't exist on the first CI run
- screenshot specs are new and have never had baselines generated
- CI is configured to run the full suite including visual specs
Not for
- snapshot mismatches where the baseline exists (that is visual drift; review the diff)
- screenshots disabled or skipped in CI (an environment problem, not a baseline problem)
- flaky pixel diffs on existing baselines (stabilize fonts and animations first)
Steps
1. Generate the baselines with --update-snapshots
Run the screenshot specs locally with the update flag. Every missing snapshot is created from the current rendering.
npx playwright test --update-snapshotsExpected output: snapshot PNG files appear next to the specs (or in the configured snapshot path).
2. Match CI's rendering environment before generating
Fonts, OS text rendering, and viewport size all change pixels. Generate on Linux if CI is Linux, with the same viewport the config uses.
// playwright.config.ts
export default defineConfig({
use: { viewport: { width: 1280, height: 720 } },
});docker run --rm -v "$PWD":/app -w /app mcr.microsoft.com/playwright:v1.55.0-noble \
npx playwright test --update-snapshotsExpected output: the baselines are generated in the same rendering environment CI uses.
3. Commit the snapshot files
Snapshots are test fixtures: they belong in git. An untracked snapshot directory is the reason the next CI run fails again.
git add -A && git status --short | grep -i snap | headExpected output: the snapshot files are tracked and pushed with the specs that reference them.
4. Configure CI to fail on missing snapshots, never create them
Set updateSnapshots to 'none' in CI so a missing snapshot is a loud failure, and a pixel diff is a regression signal rather than an auto-accepted baseline.
export default defineConfig({
snapshotPathTemplate: '{testDir}/__snapshots__/{testFilePath}/{arg}{ext}',
updateSnapshots: process.env.CI ? 'none' : 'missing',
});Expected output: CI fails loudly on missing snapshots and on genuine visual drift, and never writes baselines by itself.
Tool compatibility
- Playwright 1.40 - 1.55
- Chromium, Firefox, or WebKit screenshot assertions
Variant phrasings
- A snapshot doesn't exist on the first CI run
- playwright toHaveScreenshot snapshot missing in CI
- expect.toHaveScreenshot failed: snapshot doesn't exist
Why it happens
Playwright treats a missing baseline as an error rather than an opportunity because auto-creating baselines in CI would bless whatever CI happened to render, including regressions. Baselines must be a deliberate human-approved artifact, generated once and committed, so that every later run compares against intent rather than accident.
Edge cases / pitfalls
- generating on macOS retina and running on CI linux produces false diffs from font rendering and device pixel ratio; generate where CI runs
- dynamic content (timestamps, random ids, live data) must be masked or stubbed or every run diffs
- use toHaveScreenshot with mask locators for animated regions rather than disabling animations globally
- snapshot files are binary: configure git LFS or accept the repo growth deliberately, and review image diffs in PRs like code
Provenance
Resolved from the public thread: https://vectle.com/posts/pstKjFLR0dFCwucftbF2UzYg