Skip to content
End To End Tester

Traces and Debugging CI Failures

A trace records every action, request, console message and DOM snapshot of a run — the single artefact that turns an unreproducible CI failure into a five-minute diagnosis.

3 min read · updated 19 September 2026

A trace is a recording of everything a test did: actions, network, console, and a DOM snapshot at each step. It is the reason a Playwright failure in CI is usually diagnosable in five minutes rather than an afternoon.

If you configure one thing from this section, configure this.

typescript
// playwright.config.ts
use: {
  trace: 'on-first-retry'
}

Green runs cost nothing. A failure produces a trace.zip that contains enough to answer almost any question about what happened.

#Opening one

bash
npx playwright show-trace trace.zip

Or drag the zip onto trace.playwright.dev, which runs entirely client-side — nothing is uploaded, which matters if the trace contains session tokens.

#What it answers

The panels correspond to the questions people actually ask when a test fails:

"What was on screen?" — Every action has a DOM snapshot before and after. Not a picture: the real DOM, inspectable with developer tools. You can hover an element and see whether it was there, whether it was disabled, and what its computed attributes were.

"Why did it time out?" — The action log shows the locator it was waiting for and what it found. Zero elements, two elements, one element that was not visible — each is a different bug and the log names which.

"Did a request fail?" — The network tab lists every request with status, timing, headers and body. The most common "mysterious" browser failure is a 500 that the UI swallowed, and this is where it appears.

"Was there a JavaScript error?" — The console tab. An uncaught exception that did not break the assertion but did break the page.

"How long did each step take?" — The timeline. A step that normally takes 40ms and took 4 seconds is the finding.

#A worked diagnosis

A test fails in CI with:

TimeoutError: locator.click: Timeout 30000ms exceeded.
waiting for getByRole('button', { name: 'Pay £27.95' })

Open the trace:

  1. Action log. The click waited 30s and found zero matching elements.
  2. DOM snapshot at that moment. There is a pay button — but its label reads "Pay £0.00".
  3. Network tab. GET /api/basket returned 200 with { "lines": [] }.
  4. Console. Nothing.

Diagnosis: the basket was empty, so the total was zero and the button's accessible name did not match. The bug is in the test's setup — the fixture created the basket for a different session — not in the checkout page.

That took about two minutes and no reproduction. Without the trace it is a timeout message and a guess.

#Tracing outside a failure

typescript
// Manual control, when you want a trace of a specific section.
await context.tracing.start({ screenshots: true, snapshots: true, sources: true });

// ... the interesting part ...

await context.tracing.stop({ path: 'trace.zip' });
typescript
// Chunks: one trace per test in a shared context.
await context.tracing.startChunk({ title: test.info().title });
// ...
await context.tracing.stopChunk({ path: `traces/${test.info().title}.zip` });

#In CI

yaml
- run: npx playwright test
- uses: actions/upload-artifact@v4
  if: always()                    # the trace is needed precisely when it failed
  with:
    name: traces-${{ matrix.shard }}
    path: test-results/
    retention-days: 7

if: always() rather than if: failure() if you also want the traces from a run that was cancelled — a cancelled run often has the most interesting hang.

#What other tools offer

Tool Equivalent
Playwright Trace Viewer — the most complete
Cypress time-travel command log with DOM snapshots; very good interactively, less portable
Selenium BiDi logs and CDP capture, assembled yourself
Appium server logs plus a page-source dump; no unified artefact

Cypress's in-runner experience is arguably nicer than Playwright's while you are watching; Playwright's wins for CI because the artefact is a portable file you can open later on another machine.

#Debugging locally

bash
npx playwright test --debug              # inspector, step through
npx playwright test --ui                 # watch mode with a built-in trace view
PWDEBUG=console npx playwright test      # browser open with playwright helpers
npx playwright codegen https://localhost:3000   # generate selectors by clicking

The UI mode is the one to reach for day to day: it runs tests, shows the trace live, and lets you re-run a single test against a paused browser.

#The habit worth building

When a test fails in CI, open the trace before you try to reproduce it locally. Local reproduction of a CI failure is often impossible — different timing, different data, different machine — and the trace frequently contains the answer outright.

The teams that find flakiness manageable are almost always the ones who do this.

Common questions

What is in a Playwright trace?
Every action with its duration and result, a DOM snapshot before and after each one, all network requests with headers and bodies, console messages, the test source with the failing line highlighted, and a screencast filmstrip. It is enough to diagnose most failures without reproducing them.
Does tracing slow the tests down?
Yes, noticeably — which is why trace on-first-retry is the standard setting. Green runs pay nothing, and the trace exists exactly when a test was behaving unpredictably.
Can I open a trace without installing anything?
Yes. trace.playwright.dev runs entirely in the browser and does not upload the file anywhere. Drag the zip in and it opens.

Runnable samples for this page

last test results ↗

Working tests, not fragments — they run in CI on every push to 8exgh/endtoendtester-samples.

Was this page useful?