Quick answer
Playwright failure output has four parts: the error message, the call stack, the step log, and (if configured) a trace.zip. Open the trace with npx playwright show-trace and scrub — it shows the DOM, network, and console at every step. 9 out of 10 failures are diagnosed inside Trace Viewer.
1. Anatomy of a failure message
Here's a typical failure. Read it top-down:
1) [chromium] › tests/todo.spec.ts:12:3 › adds a todo to the demo app
Error: expect(locator).toHaveText(expected)
Locator: locator('[data-testid="todo-title"]')
Expected: "Write my first Playwright test"
Received: "Write my frist Playwright test"
Timeout: 5000ms
Call log:
- expect.toHaveText with timeout 5000ms
- waiting for locator('[data-testid="todo-title"]')
- locator resolved to <li data-testid="todo-title">…</li>
- unexpected value "Write my frist Playwright test"
12 | await input.press('Enter');
13 | const todo = page.getByTestId('todo-title');
> 14 | await expect(todo).toHaveText('Write my first Playwright test');
| ^
15 | });
attachment #1: screenshot (image/png) ────────────
test-results/todo-adds-a-todo/test-failed-1.png
attachment #2: trace (application/zip) ──────────
test-results/todo-adds-a-todo/trace.zip- Line 1 — browser · file · line · title. Copy this into your ticket.
- Expected vs Received — the actual diff. Here: a real typo in the app.
- Call log — every retry Playwright made until timeout.
- Code frame — the exact line, arrow-pointed.
- Attachments — screenshot at failure + trace.zip for time-travel.
2. Trace Viewer — the fastest debug tool ever built
- 1
Open the trace
npx playwright show-trace test-results/todo-adds-a-todo/trace.zip # or from the HTML report npx playwright show-report - 2
Scrub the timeline
Every action becomes a step in the top timeline. Click one and the browser snapshot on the right shows the exact DOM at that moment.
- 3
Use Actions / Metadata / Console / Network tabs
- Actions — what Playwright did.
- Metadata — browser, viewport, run duration.
- Console — page-side JS errors (often the smoking gun).
- Network — every request/response with headers and payload.
- Source — the test file with the failing line highlighted.
- 4
Pick a locator against the snapshot
Click the "Pick locator" button and hover the failing element in the snapshot. Playwright suggests a stable selector — copy it into the test.
3. Common error messages and what they mean
| Error | Real meaning | Fix |
|---|---|---|
| strict mode violation: resolved to 3 elements | Locator matched more than one node | Add getByRole + name, .first(), or .filter({ hasText }) |
| Timeout of 30000ms exceeded | Test as a whole took too long | Split test or increase test timeout in config |
| locator resolved but action timed out | Element exists but is not clickable (overlay / disabled / animating) | Wait for toBeVisible + toBeEnabled first, or scroll into view |
| Target page, context or browser has been closed | Test ended (probably threw) before assertion completed | Add missing await; check for early return |
| expect(received).toEqual(expected) | Values differ — real assertion failure | Read Expected vs Received diff carefully |
| net::ERR_CONNECTION_REFUSED | App not running on that host/port | Start the dev server or check baseURL |
4. Bug vs flaky vs locator drift — a 30-second triage
- 1
Re-run the failing test 3× locally
npx playwright test --repeat-each=3 -g "test name". Fails every time → probably a real bug. Fails sometimes → flaky. - 2
Open the trace
Real bug → the Expected/Received diff points at product behaviour. Flaky → the step succeeded on retry, or a network call was pending.
- 3
Check the locator
If the failing locator no longer exists in the DOM snapshot → locator drift. Redesign made the selector invalid. Pick a new stable locator.
5. Playwright config for great debug output
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
timeout: 30_000,
expect: { timeout: 5_000 },
retries: process.env.CI ? 1 : 0,
reporter: [['html', { open: 'never' }], ['list']],
use: {
baseURL: process.env.BASE_URL ?? 'http://localhost:3000',
trace: 'on-first-retry', // full trace only when needed
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
],
});playwright-report/ and test-results/ as workflow artifacts. Every failure is then a one-click download for the whole team.6. Hands-on task (10 minutes)
- 1
Break a test on purpose
Open your todo test from Lesson 3. Change the expected text to have a typo. Run it.
- 2
Read the failure top-down
Identify: Expected, Received, Call log, code frame. Write those four values down.
- 3
Open the trace
npx playwright show-report - 4
Fix it and re-run
Restore the correct expected text and confirm green. Bonus: introduce a duplicate selector to reproduce the 'strict mode violation' error.
7. What's next
You can now read any failure with confidence. Module 2 starts with the single most important skill in Playwright: Playwright Locators — role, label and text.
Docs: Trace Viewer · Debugging