skip to content

Retry Attempts

Re-running a failed test a fixed number of times, and the flaky verdict a pass-on-retry earns. Interviewers ask because a retry is worthless unless the first attempt left evidence.

on this pageshow

explore

questions

4

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
open as a page

In Playwright, does a CI run end green when three tests failed once and passed on retry?

level: middleimportance: must knowfreq 55%

basics

~20 s

Yes. Playwright records a test that failed then passed within its retries as flaky, counts it separately in the report, and still exits with code zero, so the job goes green unless the run is started with --fail-on-flaky-tests.

open as a page

In a Playwright test, what does testInfo.retry hold, and why would a hook read it?

level: middleimportance: should knowfreq 45%

basics

~20 s

testInfo.retry is the zero-based attempt number of the running Playwright test: 0 on the first run, 1 on the first retry, and so on. Hooks read it to reset state or raise logging only when a test is being re-run.

open as a page

Your Playwright suite runs with retries: 2 and trace 'on-first-retry', yet flaky tests only leave traces of passing runs — why?

level: seniorimportance: should knowfreq 40%

basics

~10 s

Because on-first-retry starts recording on the second attempt, not the first: the run that actually failed was never traced. Switching to retain-on-first-failure records the original run and keeps it only when it failed.

open as a page