skip to content

In Playwright, what does setting retries: 2 in playwright.config.ts do to a failing test?

level: juniorimportance: must knowfreq 70%

answer

  1. Attempts equal retries plus one
  2. Only the failing test re-runs
  3. Config value, or a command-line override
  4. A later pass earns its own label
  5. Green exit code can still hide re-runs

basics

~20 s

Playwright re-runs a failing test up to two more times, so retries: 2 allows three runs in all. Only the failing test re-runs, and one that passes on a later attempt is reported as flaky rather than failed.

solid answer

~40 s

`retries` is the number of extra attempts after the first, so `retries: 2` means at most three runs of that test. It defaults to `0`. You can set it at the top level of `playwright.config.ts`, per entry in `projects`, per group with `test.describe.configure({ retries: 2 })`, or override it for one run with `--retries=2`. Only the test that failed is re-queued, and each attempt starts clean: new browser context, new `page`, hooks re-run. Retrying stops as soon as an attempt passes, so it is a ceiling rather than a quota. A test that fails then passes is reported **flaky** and does not by itself make the run exit non-zero; one that fails every attempt is reported failed and does.

code

typescript · 8 lines
typescript
import { defineConfig } from '@playwright/test';

export default defineConfig({
  retries: process.env.CI ? 2 : 0,
  use: {
    trace: 'retain-on-first-failure',
  },
});

go deeper

for a junior

Remember the arithmetic: retries is the number of extra attempts, so retries: 2 allows three runs. The default is zero, and only the test that failed runs again.

for a middle

Explain the mechanics: where the value can be set, that each attempt gets a fresh context and re-runs its hooks, and that retrying stops on the first passing attempt.

for a senior

Show that you know what a retry does not reset. Rows written to the payroll database by the failed attempt survive, so retries are only trustworthy when setup is idempotent.

for a principal

Own the trade: retries buy a second data point about reproducibility, and they cost wall-clock time on exactly the slowest tests plus a green exit code that hides instability unless something reads the flaky count.

## What the `retries` option sets `retries` is the number of **extra attempts** Playwright gives a test after its first run fails — it is not the total number of runs. `retries: 2` therefore allows up to three runs of the same test: the original attempt plus two retries. The default is `0`, so an out-of-the-box Playwright run never re-runs anything, and a single timed-out locator ends the test there. The value can come from several places, and the more specific one wins: - `retries: 2` at the top level of `playwright.config.ts`, applying to every project; - `retries` inside one entry of the `projects` array, so the Chromium leg of a payroll matrix can retry while another leg does not; - `--retries=2` on the command line, which overrides the config file for that run; - `test.describe.configure({ retries: 2 })` inside a file, scoping the value to one group. ## What happens on a failing attempt 1. The test body throws — an assertion fails, a locator's actionability wait times out, or a hook errors. 2. Playwright records that attempt together with whatever artifacts the `use` block asked for, in that attempt's own output directory. 3. If attempts remain, the test is queued again and runs **from scratch**: a fresh browser context, a fresh `page`, and every `beforeEach` re-executed. 4. The moment an attempt passes, retrying stops. `retries` is a ceiling, not a quota — a test that passes on attempt two never runs a third time. 5. If the last allowed attempt still fails, the test is reported failed and the process exits non-zero. Retrying is per test, not per run. Only the test that failed is re-queued; the several thousand payroll cases that already passed are not touched, which is why a suite with retries enabled is not three times slower — it is slower only by the cost of the failures. ## The three outcomes | What happened | Reported as | Effect on the exit code | |---|---|---| | Passed on the first attempt | passed | none | | Failed, then passed on a later attempt | flaky | none by default | | Failed on every allowed attempt | failed | run exits non-zero | The middle row is the one candidates miss. A pass on a retry is not silently upgraded to a plain pass: Playwright keeps it as a separate **flaky** verdict with its own count in the reporters and its own tab per attempt in the HTML report. ## What a retry resets, and what it does not Everything Playwright owns is rebuilt for the new attempt: - a new browser context, so cookies, `localStorage` and permissions start clean; - a new `page`, so the test begins at `about:blank` and must navigate again; - test-scoped fixtures, torn down after the failed attempt and constructed again; - the failing test's worker process, which Playwright replaces, so module-level variables in the test file start at their initial values. Everything outside the process survives: - rows the first attempt wrote to the payroll database, including the half-approved pay run that made the assertion fail; - files on disk, queue messages, and state in the application under test; - uniqueness constraints — an employee record created by attempt one will collide when attempt two creates it again. That asymmetry decides whether retries are useful at all in a large regression suite. If setup is idempotent — seeding through an API, namespacing fixture data per attempt, or resetting before each test rather than after — the second attempt starts from the same place the first did, and a pass genuinely means the failure was timing. If setup is not idempotent, retries produce a second, different failure that has nothing to do with the original one, and the report becomes harder to read rather than easier. ## Reading a retried run A run with retries answers a narrower question than people assume: it says whether the failure reproduces immediately, on the same machine, against the same build. It does not say why the first attempt failed. That evidence has to have been captured **during the attempt that failed**, which is why `retries` is nearly always configured alongside the artifact options in `use` — a trace or video mode that records the first run rather than only the re-run.

  • If a test passes on its second attempt with retries: 2, does Playwright still run the third?
    No. Retrying stops the moment an attempt passes, so `retries` is an upper bound rather than a fixed number of runs. The test is then reported flaky, with both attempts kept in the report under their own tabs.
  • Does a run that ends with three flaky tests and no failures exit non-zero?
    Not by default — Playwright's exit code asks whether every test eventually passed, so the job goes green. Passing `--fail-on-flaky-tests` on the command line makes any flaky test produce a non-zero exit code instead.

saying these in an interview costs you the question

  • Thinks retries: 2 means two attempts in total
  • Believes a retry re-runs the whole suite or file
  • Assumes a flaky test makes the run exit non-zero
  • Says passing tests are retried as well
  • Thinks retries are enabled by default
  • Claims a retry continues in the same browser context