In Playwright, what does the config option trace: 'on-first-retry' record?
answer
- Cost only when something already failed
- One attempt out of two is captured
- Not the failing attempt, the next one
- Useless when retries are zero
basics
~10 sPlaywright records a trace only while a test runs its first retry. The original failing attempt is never traced, and if the project allows no retries, no trace is produced at all.
solid answer
~40 s`trace: 'on-first-retry'` in the `use` block of `playwright.config.ts` tells the test runner to switch tracing on for exactly one attempt: the first retry of a test that already failed once. Attempt one runs untraced, so a suite that mostly passes pays nothing; when something fails, the retry produces a `trace.zip` in that attempt's output folder and attaches it to the report. Two things follow. It needs `retries` above zero — with no retries the traced attempt never happens. And the zip shows a re-run, not the original failure, so a flake that does not reproduce leaves you with a trace of a passing attempt. That trade — near-zero cost on green runs, evidence only for reproducible failures — is why it is the mode most suites start with.
code
typescript · 9 linesimport { defineConfig } from '@playwright/test';
export default defineConfig({
retries: process.env.CI ? 2 : 0,
use: {
baseURL: 'https://orders.example.com',
trace: 'on-first-retry',
},
});go deeper
Remember the shape: no trace on the first attempt, a trace on the first retry. If you set this mode and see no zip after a failure, check that retries are enabled.
Be able to explain that the option is a use option resolved per project, per file and per command line, and that the captured attempt is a re-run rather than the original failure.
Show you weigh capture cost against evidence value, and that you know this mode is blind to flakes that pass on retry — say what you would switch to when chasing one.
Own the policy: which projects record what, how the mode interacts with the retry budget, and how you stop a debugging setting like always-on tracing from silently becoming the suite default.
A **trace** is a zip file Playwright writes for one test attempt: an action-by-action timeline, DOM snapshots taken around each action, console messages, network records and optionally your test sources. The `trace` option decides *which attempts get one*, and `'on-first-retry'` is the narrowest useful setting. ## What the mode does, step by step 1. The test runs its first attempt with tracing **off**. 2. If it passes, nothing is recorded and nothing is written — a green run costs no capture time and no disk. 3. If it fails and the project allows retries, the runner re-runs the test and turns tracing **on** for that second attempt. 4. When the attempt ends, the zip is written to that attempt's folder under `test-results/` as `trace.zip` and registered as an attachment named `trace` on the test result. The crucial detail is *which* attempt is captured. `'on-first-retry'` never traces the attempt that originally failed; it traces a reproduction of it. ## Where the option lives It is a `use` option, so it can sit at the top level of the config or be narrowed per project: ```ts export default defineConfig({ retries: process.env.CI ? 2 : 0, use: { trace: 'on-first-retry' }, projects: [ { name: 'order-tracker-chromium', use: { ...devices['Desktop Chrome'] } }, { name: 'order-tracker-mobile', use: { ...devices['Pixel 7'], trace: 'on' } }, ], }); ``` - A per-project `use` value **wins** over the top-level one, so one noisy project can record more than the rest. - `test.use({ trace: 'on' })` inside a file narrows it further, down to a describe block. - The command line wins over the config for that run: `npx playwright test --trace on`. - The value may also be written as an object — `trace: { mode: 'on-first-retry', sources: false }` — when you want the mode plus control over what goes inside the zip. ## Why suites reach for it Tracing is not free. Every recorded action stores a snapshot of the page, so traces are usually the largest thing a run produces — far heavier than a log line or a single image. `'on-first-retry'` inverts the cost model: the common case (everything green) records nothing, and capture only switches on for the small set of tests that already misbehaved once. For a food-delivery order tracker, where a suite drives live status polling and a driver map on every push, that difference is the gap between a fast pipeline and one that spends most of its time serialising page snapshots for tests that passed. ## What you give up | you want | `'on-first-retry'` gives you | |---|---| | evidence of the exact failing attempt | no — attempt one is untraced | | evidence of a deterministic bug | yes — the retry reproduces it faithfully | | evidence of a flake that passes on retry | a trace of a **passing** run, which rarely explains anything | | evidence when `retries: 0` | nothing at all | That last row is the most common surprise. Developers run locally with retries disabled, see a failure, look for a trace and find none — the mode is working exactly as specified. ## Choosing it, and when not to - Keep `'on-first-retry'` when failures are mostly reproducible and pipeline time matters. - Move to a retain-style mode when you need the *original* attempt captured rather than a re-run. - Move to `'on'` only for a small, deliberately scoped project or a debugging session — it records and keeps every attempt. - Remember that the mode says nothing about how many retries you get; that is the separate `retries` setting, and the two must be configured together for this mode to mean anything. A final practical note: with the test runner, tracing is started and stopped **for** you. You do not call the tracing API yourself on the fixture-provided context when a `trace` mode is set — the runner already owns that context's tracing, and doing both fights over the same recording.
- If a test fails, is retried and passes, what does the trace you get actually contain?The retry that passed. Tracing was off for the failing attempt, so the zip is a recording of a green run: the actions all succeed and there is no error to inspect. That is the known blind spot of the mode for flaky tests, and the reason a suite chasing flakes usually needs a retain-style mode instead.
- How would you keep 'on-first-retry' for the suite but always trace one problem spec?Narrow the option where the problem lives. Add `test.use({ trace: 'on' })` at the top of that spec or inside its describe block, or give the spec its own project with `use: { trace: 'on' }` and a matching `testMatch`. The `use` value closest to the test wins, so the rest of the suite keeps the cheap mode.
saying these in an interview costs you the question
- Thinks the failing first attempt is the one traced
- Expects traces with retries left at zero
- Believes on-first-retry traces every retry attempt
- Assumes tracing is free and always worth enabling
- Calls the tracing API manually while the trace option is set