## TL;DR

The download fails from network blocks, partial caches, or missing install steps. Install with `--with-deps` on a fresh image, cache the browser directory between runs, and pin the Playwright version so the cache key stays valid.

## Error

```text
Error: Download failed: server returned code 403
    at IncomingMessage.[anonymous] (playwright-core/lib/server/registry)
```

## Steps

1. Run `npx playwright install --with-deps chromium` verbosely to see the real failure. Expected: the underlying HTTP or filesystem error.
2. If the registry is blocked, set `PLAYWRIGHT_DOWNLOAD_HOST` to a mirror. Expected: downloads come from the allowed host.
3. Cache `~/.cache/ms-playwright` between CI runs keyed on the Playwright version. Expected: downloads happen once, not every run.
4. Pin the Playwright version in package.json; a floating version invalidates the cache constantly. Expected: cache hits on every run.
5. Verify with `npx playwright install --dry-run` that the right browsers are present. Expected: no downloads needed on a warm cache.

## When to use

- `playwright install` fails in CI but works locally.
- New CI image or Playwright version bump.

## When not to use

- Browsers install but fail to launch (missing OS deps; use --with-deps).
- You use system browsers instead of bundled ones.

## Tool compatibility

- Playwright 1.30 through latest; `PLAYWRIGHT_DOWNLOAD_HOST`, `--with-deps`.

## Variant phrasings

### Playwright install 403 in CI

Registry blocked; mirror or allowlist.

### ms-playwright cache miss every run

Cache key problem; pin the version.

## Why it happens

Playwright downloads real browser binaries separately from npm. CI networks block new hosts, caches miss on version drift, and fresh images lack the install step.

## Edge cases

- `--with-deps` needs root or sudo in the image; split install into a privileged step.
- Firefox and WebKit have separate system deps; install all three browser dep sets if you test all three.
- Docker layer caching and CI caching are different; use both.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_iuX8IuJXZnQ0BLp7hIeGZg
