Start with the failure output
Playwright's default test output tells you which test failed, which assertion was not met, and often includes a screenshot of the browser state at the moment of failure. Read this carefully before opening the browser or adding debug code — the output alone resolves a significant proportion of failures.
The most common failure messages tell a clear story: 'locator not found' means the selector did not match any element. 'Timeout exceeded' means Playwright waited for something that never appeared. 'Expected value X but received Y' means the assertion condition was not met. Each of these points to a different root cause.
Use the Playwright Trace Viewer
The Playwright trace viewer is the most powerful debugging tool available. It records every browser action, DOM snapshot, network request, and console log during a test run and lets you replay them step by step in a visual interface.
Enable tracing in your Playwright config with `trace: 'on-first-retry'` or `trace: 'retain-on-failure'`. After a failing run, open the trace with `npx playwright show-trace trace.zip` and step through the test to see exactly what the browser was doing at each point.
- Step through each action and see the exact DOM state before and after
- Inspect network requests to identify API failures that cause UI state to be wrong
- View console errors that only appear in browser context
- See screenshots at every action, not just on failure
Use the Playwright Inspector for live debugging
Run `PWDEBUG=1 npx playwright test` to open the Playwright Inspector alongside the browser. The inspector lets you step through test actions one at a time, pause execution at any point, and try selector queries interactively against the live page.
This is particularly useful when a selector is not matching as expected. You can try different selector strategies directly in the inspector without editing and re-running the test each time.
Common failure patterns and their fixes
Most Playwright test failures fall into a small number of categories. Selector failures occur when the element the test is looking for does not exist or does not match the selector — usually caused by a UI change or an incorrect selector strategy. Timing failures occur when the test proceeds before the UI has finished responding — usually caused by missing waits or a changed loading behaviour. Assertion failures occur when the UI reaches the expected state but with different content than expected — usually caused by a product change that was not reflected in the test.
- Selector not found: the element moved, was renamed, or your selector is wrong — use the trace viewer to see what was actually on the page
- Timeout: something the test is waiting for never appeared — check whether the previous action completed successfully
- Wrong URL after action: a redirect changed — update the assertion to match the new destination
- Intermittent failure: a race condition or flaky animation — add an explicit wait for a stable UI signal before proceeding
- Authentication failure: test credentials expired or the login flow changed — refresh the stored auth state
Preventing failures before they happen
Many Playwright failures are preventable with better authoring habits. Using role-based selectors instead of CSS classes significantly reduces selector drift. Adding explicit waits for meaningful UI state — rather than fixed delays — reduces timing failures. Keeping tests focused on a single flow makes failures easier to attribute and fix.
Assert's approach of generating the Playwright layer from a human-readable scenario means selector-level issues are handled by the generation layer rather than by the test author. When a selector fails, the scenario can be updated at the intent level and regenerated rather than requiring manual selector surgery.
FAQ
Why does my Playwright test pass locally but fail in CI?
The most common causes are: a slower CI environment that triggers timing failures that do not appear locally, environment differences like missing environment variables or different base URLs, and browser version differences. Enable tracing in CI and compare the trace from a failing run to a successful local run to identify where the behaviour diverges.
How do I debug a flaky Playwright test?
Run the test with `--repeat-each=5` to reproduce the flakiness reliably, then enable tracing to capture what differs between passing and failing runs. Flakiness usually points to a race condition — something the test assumes is ready before it actually is. Add an explicit wait for the specific UI state rather than a fixed timeout.
What is the fastest way to find a broken selector?
Open the Playwright Inspector with `PWDEBUG=1`, navigate to the page manually, and use the selector playground to test your selector against the live DOM. You will see immediately whether it matches and how many elements it finds.
Put the workflow in your repo, not in a chat transcript
Assert is strongest when scenarios become durable project assets: readable Markdown in the repo, generated execution underneath, and result inspection in the dashboard.