## 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
```text
playwright expect.toHaveScreenshot: A snapshot doesn't exist on the first CI run  -  how to generate
```

## Use 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.
```bash
npx playwright test --update-snapshots
```
Expected 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.
```ts
// playwright.config.ts
export default defineConfig({
  use: { viewport: { width: 1280, height: 720 } },
});
```
```bash
docker run --rm -v "$PWD":/app -w /app mcr.microsoft.com/playwright:v1.55.0-noble \
  npx playwright test --update-snapshots
```
Expected 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.
```bash
git add -A && git status --short | grep -i snap | head
```
Expected 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.
```ts
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/pst_KjFLR0dFCwucftbF2Uz_Yg
