Find out what killed the browser process. "Browser has been closed" mid-suite almost never means your test closed it - it means something outside the test did: the container ran out of memory and the OOM killer shot the browser, a fixture tore down a shared browser while other tests still needed it, or too many parallel workers starved the machine. Check CI memory metrics and dmesg-style logs first; if the browser died from memory pressure, cut workers or raise the container limit. If the timing lines up with a fixture teardown, the sharing is wrong. Guessing between these wastes hours - the logs tell you which one it is.

```text
playwright "browser has been closed" error mid-suite in CI: what causes it
```

## Steps
1. Look at when the error hits: same test every run (fixture or code problem) or random tests at random times (resource problem). Randomness points at memory.
   Expected: you have classified the failure as deterministic or random.

2. Check CI memory: look at the container memory limit and peak usage in the CI logs. If the browser dies near the limit, the OOM killer is your culprit - add the flag that moves shared memory off the small default partition, or lower the worker count.
   Expected: memory pressure is confirmed or ruled out with numbers, not guesses.

3. Audit browser lifecycle: search fixtures and hooks for any close call on a shared browser or context. A fixture that closes the shared browser in teardown while another worker still uses it produces exactly this error.
   Expected: every close call is accounted for and shared browsers are closed exactly once, at the very end.

4. Reduce parallelism as a diagnostic: run CI with one worker. If the error disappears, you have resource contention - then decide between fewer workers permanently or a bigger CI machine.
   Expected: a single-worker run is green, proving contention rather than code.

5. Make the fix structural: set a sensible worker count for the CI machine size, keep browser launch args that survive small containers, and never share-then-close a browser across workers.
   Expected: the suite passes at full parallelism three runs in a row.

## Use this when

- "browser has been closed" appearing mid-suite in CI while local runs are fine
- random tests dying at random points in CI
- you need to separate OOM kills from fixture teardown bugs

## Not for this skill when

- the error at suite start before any test runs (that is a launch problem - browsers not installed or sandbox flags)
- your own test code explicitly closing the browser (that is visible in the test - just do not close it early)

## Variant phrasings

### playwright browser closed unexpectedly

Same symptom family. Mid-suite points to resources or teardown; at-start points to installation or launch flags.

### playwright target closed

The older wording of the same event - the browser process went away while tests still needed it.

## Why it happens

The browser is a separate OS process from the test runner. In CI it runs inside a container with hard memory limits, and browsers are hungry: each worker launches full browser processes with renderers and GPU processes. When the container hits its limit, the kernel OOM killer picks the biggest process - usually the browser - and the test runner only learns about it when its next command fails with "browser has been closed". Separately, Playwright fixtures that share one browser across tests will produce the same error if any teardown closes it early.

## Edge cases

- the error only on one CI provider: compare container memory defaults - some providers give much less memory per runner than others
- headed versus headless: headed browsers need a display server and more memory; CI should run headless unless the test needs headed
- video and trace recording on every test: these inflate memory and disk - record on failure only in CI

## Provenance

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