skip to content

What happens to a Playwright worker process when one of its tests fails?

level: middleimportance: must knowfreq 58%

answer

  1. Failure means the process is suspect
  2. The whole process goes, not just the page
  3. A fresh browser for the remaining tests
  4. Worker fixtures are set up again
  5. Red runs pay extra start-up time

basics

~20 s

Playwright throws the worker away. The process is shut down after the failure and a fresh worker process, with a new browser, picks up the remaining tests, so nothing the failing test left behind reaches them.

solid answer

~50 s

A test failure is treated as evidence that the process is no longer trustworthy, so Playwright shuts that worker down and starts a new one to continue the run. Everything the process held goes with it: module-level variables in the imported test files, the browser it launched, and any worker-scoped fixture, which is torn down and then set up again in the replacement. The new process gets a fresh `testInfo.workerIndex`, so you can see the restart in logs and reports. The benefit is a guarantee: no test ever inherits a process that a failing test left half-way through a login, mid-transaction, or leaking memory. The cost is real too — a failing payroll run pays browser start-up and worker set-up again for each failure, so a red run is measurably slower than a green one.

code

typescript · 16 lines
typescript
import { test, expect } from '@playwright/test';

let testsInThisProcess = 0;

test.beforeEach(({}, testInfo) => {
  testsInThisProcess += 1;
  console.log(`${testInfo.title}: pid=${process.pid} worker=${testInfo.workerIndex} n=${testsInThisProcess}`);
});

test('payroll: payslip totals (fails on purpose)', async () => {
  expect(1).toBe(2);
});

test('payroll: next test starts in a brand new process', async () => {
  expect(testsInThisProcess).toBe(1);
});

go deeper

for a junior

Remember the headline: a failed test costs the whole worker process, and the next test starts in a new process with a new browser. Nothing kept in memory carries over.

for a middle

Explain the mechanics — the process is discarded, files are imported again, worker-scoped set-up reruns and the worker index changes — and say why guessing which failures are recoverable is unsafe.

for a senior

Connect it to operations: red runs are slower because each failure buys a cold browser launch, per-worker set-up must be idempotent, and data the failing test wrote to the payroll environment still needs cleaning up.

for a principal

Frame the trade you accept: deterministic isolation paid for in restart cost. Argue where the lever actually is — cheap worker start-up and a low failure rate — rather than looking for a way to reuse dirty processes.

## The rule When a test fails, Playwright does not simply record the result and hand the next test to the same process. It **discards the entire worker process** and starts a replacement to continue with the remaining tests. The behaviour is unconditional: the worker options control how many processes you get, not whether a process that has just failed a test is reused. The reasoning is that a failed test is, by definition, a test that did not finish the way the author expected. It may have left a modal open, a request in flight, a listener attached, a global variable half-mutated, or the browser in a state nobody has ever seen. Sorting the recoverable cases from the unrecoverable ones is impossible from outside, so Playwright refuses to guess and throws the process away instead. ## What dies with the worker - **Module-level state** in every test file that worker imported — counters, memoised clients, lazily-built fixtures data. - **The browser** the worker launched, along with every context and page still open in it. - **Worker-scoped fixtures**, which are torn down as the worker shuts down and set up again from scratch in the replacement. - **Anything the process cached in memory**: a signed-in payroll session token, a warmed-up API client, a parsed fixtures file. ## What does not die - Rows the failing test wrote to the payroll database, files it left on disk, and messages it sent to a queue. These live outside the process and survive untouched. - The run's results so far. The reporter is part of the main runner process, not the worker, so results already reported stay in the report. - The overall run. Remaining tests continue in the fresh worker; a failure ends the process, not the suite. ## How the restart shows up | Signal | What you see | |---|---| | `testInfo.workerIndex` | a new, higher index for the replacement process | | `testInfo.parallelIndex` | unchanged — the replacement takes over the same slot | | Worker-scoped set-up | runs a second time for the same run | | Wall-clock time | an extra browser launch on the critical path | A quick way to prove the mechanism to yourself is a module-level counter incremented in `beforeEach`. In a file where the first test fails, the second test sees the counter at `1`, not `2` — the file was imported again into a brand-new process. ## What it means for a payroll regression suite 1. **Never lean on test order to carry state.** Even inside one file, the process that ran the earlier test may be gone by the time the later one starts, so a test that assumes an earlier one left something in memory is not merely fragile — it is wrong. 2. **Expect red runs to take longer than green ones.** Every failure buys another cold browser launch plus whatever your per-worker set-up costs. A suite with forty failures pays that forty times, which is why a broken CI run often looks like it also got slower. 3. **Keep per-worker set-up cheap and idempotent.** It will run again, possibly many times, and it must tolerate being re-entered against a payroll environment where its previous incarnation already seeded data. 4. **Read restarts as a signal.** A run whose worker indexes climb far past the configured worker count is a run with many failures — worth noticing even before you read the report. ## The trade you are being handed The design buys **determinism at the cost of throughput**. A run cannot produce the confusing failure mode where one broken test quietly poisons the next ten, because the next ten literally start in a process the broken test never touched. What you pay is the restart, and the lever you have is not a switch to turn it off — it is keeping failures rare and worker start-up cheap.

  • Does a worker restart mean the failing test's database writes are rolled back?
    No. The restart discards a process, not the world outside it. Rows the failing payroll test inserted, files it wrote and messages it published all survive. If you need the data cleaned up, do it yourself in teardown or by giving each worker its own data, because process death cleans up nothing beyond memory.
  • How can you tell from a report that workers were restarted a lot?
    Watch the worker index. It is unique per process for the whole run, so a run configured for four workers that reaches index thirty started thirty processes — twenty-six of them replacements. Paired with the failure count, that tells you how much of the run's wall-clock time went into re-launching browsers.

saying these in an interview costs you the question

  • Says only the page or context is recreated
  • Thinks the worker is reused after cleaning cookies
  • Assumes the failure aborts the whole run
  • Believes the restart rolls back database writes
  • Expects module-level variables to survive a failure
  • Claims restarts have no effect on run duration