Playwright Masters

Playwright Debugging: Complete Guide to Debug Failed Tests

Playwright tests can look perfect when you write them. Then you run the test and suddenly see a timeout, locator error, navigation failure, or unexpected result.

The important question is not simply:

“How can I make the test pass?”

The better question is:

“Why did the test fail?”

That is where Playwright debugging becomes important.

Table of Contents

Playwright provides several tools for finding the real cause of a failed test, including Playwright Inspector, page.pause(), VS Code debugging, UI Mode, Trace Viewer, screenshots, videos, logs, and browser developer tools.

This guide explains Playwright debugging from beginner level to real-world troubleshooting.

You will learn how to debug:

  • Failed locators
  • Timeout errors
  • Assertion failures
  • Navigation problems
  • Authentication issues
  • Popups and multiple tabs
  • Iframes
  • Network failures
  • Flaky tests
  • CI/CD failures
  • Browser launch problems
Playwright Debugging

What Is Playwright Debugging?

Playwright debugging is the process of finding the exact reason why a Playwright test does not behave as expected and then fixing the underlying problem.

For example, imagine this test:

await page.getByRole('button', { name: 'Login' }).click();

The test fails with a timeout.

A beginner may immediately increase the timeout.

A better debugging process asks:

  1. Is the page actually open?
  2. Is the Login button present?
  3. Does the button have the expected accessible name?
  4. Are there multiple Login buttons?
  5. Is the button inside an iframe?
  6. Is a popup covering it?
  7. Did authentication or navigation change the page?
  8. Is the application still loading?
  9. Is the test running against the correct environment?

The goal of debugging is therefore not to hide the error.

The goal is to understand the error.

For current debugging features, see the official Playwright debugging documentation.

Why Do Playwright Tests Fail?

A Playwright test can fail for many different reasons.

The failure does not always mean that Playwright is broken.

Often, the test, application, test data, environment, or timing is responsible.

Common causes include:

Problem

Typical symptom

Wrong locator

Locator cannot find the element

Multiple matches

Strict mode violation

Element hidden

Actionability timeout

Slow application

Timeout

Wrong URL

Navigation/assertion failure

Authentication issue

User remains on login page

iframe

Element cannot be found

Popup

Expected page is missing

Network problem

Request fails or hangs

Test data problem

Expected record does not exist

Race condition

Test passes sometimes

CI environment

Works locally but fails in CI

Browser issue

Browser fails to launch

Configuration issue

Unexpected browser/project behavior

The first debugging skill is learning to read the failure before changing the code.

A Simple Playwright Debugging Workflow

When a Playwright test fails, follow this process:

1. Run the test

Start by reproducing the failure.

npx playwright test login.spec.ts

 

2. Read the complete error

“Don’t rely only on what appears in the last line.”

Look for:

  • Failed assertion
  • Locator
  • URL
  • Timeout
  • Expected value
  • Actual value
  • Call log
  • Source-code location

3. Identify the exact failed action

For example:

waiting for getByRole('button', { name: 'Login' })

 

Now you know Playwright was waiting for the Login button.

4. Reproduce the problem

Run the test again.

If possible, run only the failing test.

5. Inspect the browser

Use headed mode, Inspector, UI Mode, or VS Code.

6. Check the locator

Ask:

“Does my locator match the element I intend to test?” 

7. Check timing

Do not immediately add waitForTimeout().

Playwright already provides auto-waiting for many actions and assertions.

8. Check the page state

Look at:

  • Current URL
  • DOM
  • Visible elements
  • Console messages
  • Network requests
  • Authentication state

9. Choose the correct debugging tool

Use Inspector for live debugging.

Use UI Mode for interactive test exploration.

Use Trace Viewer when you need to understand a recorded failure.

Use screenshots and videos when visual evidence is useful.

10. Fix the root cause

Finally, run the test again.

This process is much better than randomly increasing timeouts.

Playwright Debugging Tools

Here is a quick comparison.

Tool

Best use

Beginner friendly?

Main limitation

Playwright Inspector

Step through a live test

Yes

Requires interactive execution

page.pause()

Stop at an exact point

Yes

Mainly useful during local debugging

--debug

Quickly enter debug mode

Yes

Not ideal for CI

VS Code debugger

Breakpoints and source-level debugging

Yes

Requires VS Code setup

UI Mode

Explore and debug tests interactively

Yes

Mainly a local development workflow

Trace Viewer

Investigate completed failures

Yes

Requires a recorded trace

Screenshot

See visual state

Yes

Only shows one captured moment

Video

See browser actions over time

Yes

Less detailed than a trace

Console logs

Inspect values and application messages

Yes

Logs alone may not explain UI state

Test report

Review failed tests and attachments

Yes

Summary rather than live debugging

Browser DevTools

Inspect DOM/network/console

Intermediate

Requires browser debugging knowledge

Playwright’s official documentation recommends modern debugging workflows such as VS Code, UI Mode, Inspector, and Trace Viewer depending on the situation.

How to Use Playwright Inspector

The Playwright Inspector is a graphical debugging tool that lets you pause and step through Playwright tests.

It can show:

  • The current test action
  • Locator information
  • Actionability logs
  • Matching elements
  • Browser state
  • Locator picker

Playwright’s --debug mode opens the Inspector and launches the browser in headed mode. It also sets the default timeout to zero for debugging.

Start Playwright Debug Mode

Run:

npx playwright test --debug

 

Now Playwright opens the Inspector and browser.

You can move through the test step by step.

You can also debug one test:

npx playwright test tests/login.spec.ts --debug

 

Or target a particular line:

npx playwright test tests/login.spec.ts:15 --debug

 

This is useful when a large test file contains many tests and you only want to investigate one failure.

How Does page.pause() Work?

Sometimes you do not want to step through the entire test.

You already know approximately where the problem happens.

For example:

import { test, expect } from '@playwright/test';

test('login test', async ({ page }) => {
  await page.goto('https://example.com/login');

  await page.getByLabel('Email').fill('user@example.com');
  await page.getByLabel('Password').fill('password');

  await page.pause();

  await page.getByRole('button', { name: 'Login' }).click();

  await expect(page.getByText('Dashboard')).toBeVisible();
});

 

When Playwright reaches:

await page.pause();

 

execution stops.

You can inspect the page before continuing.

“This is especially valuable when you need to find out why:” 

  • A locator
  • A dropdown
  • A popup
  • A form
  • A dynamic element
  • A navigation state

The official Playwright debugging guide documents page.pause() as a way to create a targeted breakpoint rather than stepping through every previous action.

Debugging Locators in Playwright

Locator problems are among the most common causes of Playwright failures.

Consider:

await page.locator('.login-button').click();

The test fails.

Instead of guessing, inspect the page and ask:

Does .login-button still exist?

A better locator may be:

await page.getByRole('button', { name: 'Login' }).click();

Playwright recommends user-facing locators such as roles and labels where appropriate because they better represent how users interact with the page.

Useful locator choices

page.getByRole('button', { name: 'Login' });
page.getByLabel('Email');
page.getByPlaceholder('Enter email');
page.getByText('Welcome');
page.getByTestId('login-button');

Avoid extremely fragile selectors such as:

page.locator(
  '#app > div:nth-child(2) > div:nth-child(3) > button'
);

The more a selector depends on DOM structure, the easier it can be for a harmless UI change to break the test.

Debugging Strict Mode Violations

A common error looks like:

strict mode violation

 

This usually means your locator matched more than one element while Playwright expected one.

For example:

await page.getByRole('button', { name: 'Save' }).click();

 

Suppose the page contains two Save buttons.

Playwright cannot safely decide which one you mean.

Instead, make the locator more specific:

const settings = page.getByRole('dialog', { name: 'Settings' });

await settings.getByRole('button', { name: 'Save' }).click();

 

You can also inspect matching elements during debugging.

The important lesson is:

Do not solve a strict-mode problem by randomly choosing .first() unless the first element is genuinely the intended element.

Debugging Timeout Errors

  • A timeout does not automatically mean:

    “The application is too slow.”

    It means Playwright waited for a condition and did not get the expected result within the allowed time.

    For example:

    await page.getByRole('button', { name: 'Checkout' }).click();


    Possible causes include:

    • Button does not exist
    • Button has a different name
    • Button is inside an iframe
    • Button is hidden
    • A modal blocks it
    • User is not logged in
    • Wrong page loaded
    • Application failed to render
    • Network request failed

    Do not immediately do this:

    await page.waitForTimeout(10000);


    This adds a fixed delay but may not solve the real problem.

    Instead, determine what Playwright is actually waiting for.

    For example:

    await expect(
      page.getByRole('button', { name: 'Checkout' })
    ).toBeVisible();

    Then investigate why the condition is not becoming true.

    Playwright’s locator and assertion APIs are designed around auto-waiting and retryability, so understanding the condition being waited for is more useful than adding arbitrary sleeps.

Debugging Assertions

Suppose you have:

await expect(page).toHaveURL('/dashboard');

 

The test fails.

Do not assume the URL assertion is wrong.

First inspect:

console.log(await page.url());

 

You may discover:

https://example.com/login

 

instead of:

https://example.com/dashboard

 

Now the actual problem may be authentication.

The assertion was simply the place where the problem became visible.

This is an important debugging principle:

The failed line is not always the root cause.

Debugging Authentication Problems

Authentication failures can be confusing.

A test may appear to fail at:

await expect(page.getByText('Dashboard')).toBeVisible();

 

But the real problem happened earlier.

The application may have redirected the user back to:

/login

 

During debugging, check:

console.log(await page.url());

 

You can also inspect cookies, local storage, and the authentication flow.

If your project uses Playwright’s authentication state, review your storageState setup and make sure the saved authentication state is valid for the environment being tested.

For a broader Playwright learning path, you can connect this topic with your site’s Playwright Authentication content if that page exists.

Debugging Iframes

An iframe creates a separate browsing context.

This locator may fail:

await page.getByRole('button', { name: 'Pay' }).click();

 

because the button is actually inside an iframe.

Use a frame locator:

const paymentFrame = page.frameLocator('#payment-frame');

await paymentFrame
  .getByRole('button', { name: 'Pay' })
  .click();

 

If the locator works outside the frame but fails inside the application, always ask:

“Is the element actually inside an iframe?”

This simple question can save a lot of debugging time.

Debugging Popups and Multiple Pages

Modern applications can open:

  • New tabs
  • New windows
  • OAuth pages
  • Payment pages
  • External authentication pages

If your test expects a new page, explicitly capture it.

const newPagePromise = context.waitForEvent('page');

await page.getByRole('link', { name: 'Open payment' }).click();

const newPage = await newPagePromise;

await newPage.waitForLoadState();

 

console.log(await newPage.url());

 

If you do not capture the new page, you may continue interacting with the old page and think that the locator is broken.

Debugging Network Problems

Sometimes the UI is not the real problem.

The browser may be waiting for an API response that never arrives.

Playwright allows you to monitor network traffic. Requests made by the page, including XHR and fetch requests, can be observed and handled through its network APIs.

For example:

page.on('request', request => {
  console.log('REQUEST:', request.method(), request.url());
});

page.on('response', response => {
  console.log('RESPONSE:', response.status(), response.url());
});

 

This can help you discover:

  • 404 responses
  • 401 authentication errors
  • 403 permission errors
  • 500 server errors
  • Slow API responses
  • Incorrect API endpoints

For deeper investigation, the Network tab in Trace Viewer can show requests associated with individual actions.

Using VS Code to Debug Playwright Tests

If you use Visual Studio Code, Playwright’s VS Code integration can make debugging easier.

You can:

  • Run individual tests
  • Set breakpoints
  • Debug tests
  • See failures
  • Show the browser
  • Pick locators
  • Inspect traces

For example, your code can contain:

test('checkout', async ({ page }) => {
  await page.goto('/products');

  await page.getByRole('button', { name: 'Buy now' }).click();

  // Set a breakpoint here
  await expect(page.getByText('Checkout')).toBeVisible();
});

 

Run the test through the VS Code testing interface and use the debugger to stop at the breakpoint.

The official VS Code integration supports running and debugging tests directly from the Testing sidebar and includes features such as browser display and locator picking.

Playwright UI Mode

A useful current debugging option is UI Mode.

Start it with:

npx playwright test --ui

 

UI Mode provides an interactive interface where you can:

  • Run individual tests
  • Filter tests
  • Watch tests
  • Inspect test steps
  • View traces
  • Explore DOM snapshots
  • Move backward and forward through actions

For beginners, UI Mode can be easier to understand than reading a long terminal output because you can visually follow what happened during the test.

Debugging with Trace Viewer

Trace Viewer is especially useful when you cannot watch the test live.

Think of a trace as a flight recorder for your test.

It can help you inspect:

  • Test actions
  • DOM snapshots
  • Screenshots
  • Action timings
  • Console messages
  • Network requests
  • Source code
  • Metadata
  • Attachments

A typical CI configuration can use:

import { defineConfig } from '@playwright/test';
export default defineConfig({
  use: {
    trace: 'on-first-retry',
  },
});

You can also record traces locally:

npx playwright test --trace on

The current Playwright documentation uses on-first-retry as the default trace strategy in CI-oriented examples, rather than recording a full trace for every normal test execution.

After the test finishes, open the HTML report and inspect the trace.

When debugging complex Playwright tests, understanding how fixtures handle setup, dependencies, and cleanup can make failures much easier to troubleshoot. Learn more in our Playwright Fixtures guide.

What Should You Look for in Trace Viewer?

Suppose this action failed:

await page.getByRole('button', { name: 'Submit' }).click();

 

Open the trace.

Then inspect:

Before

Was the Submit button visible?

Action

What locator did Playwright use?

After

Did the page change?

DOM snapshot

Was the expected element actually present?

Network

Was an API request failing?

Console

Did the application report an error?

This gives you much more information than:

Test failed.

 

The Trace Viewer also lets you inspect network requests and action-specific information.

Screenshots for Playwright Debugging

Screenshots are useful when you want to know what the browser looked like at a particular moment.

For example:

await page.screenshot({
  path: 'debug-login.png',
  fullPage: true,
});

 

A screenshot can reveal:

  • Unexpected modal
  • Login page
  • Error message
  • Missing button
  • Incorrect layout
  • Wrong environment
  • Blank page

But a screenshot has a limitation.

It shows what the page looked like, not everything that happened before it.

For complicated failures, use a trace as well.

Videos for Playwright Debugging

Videos are useful when the sequence of actions matters.

For example:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    video: 'on-first-retry',
  },
});

 

Playwright supports video modes such as:

  • off
  • on
  • retain-on-failure
  • on-first-retry

A video can help you see:

  1. The page loads.
  2. A menu opens.
  3. A popup appears.
  4. The test clicks something.
  5. The application changes state.
  6. The failure happens.

However, video is visual evidence.

Trace Viewer usually provides richer debugging information because it connects actions with DOM snapshots, logs, network activity, and other test information.

Console Logging

Sometimes the easiest debugging tool is still:

console.log();

 

For example:

console.log('Current URL:', await page.url());

 

console.log(

  'Title:',
  await page.title()
);

 

You can also log test data:

console.log('Username:', username);
console.log('Order ID:', orderId);

 

For browser console messages:

page.on('console', message => {
  console.log(
    `[Browser Console] ${message.type()}: ${message.text()}`
  );
});

 

This is especially useful when the application itself reports JavaScript errors.

Important: never print passwords, access tokens, API keys, or other secrets into CI logs.

Debugging Flaky Playwright Tests

A flaky test is a test that does not produce the same result consistently under the same intended conditions.

For example:

Run 1 → Pass

Run 2 → Pass

Run 3 → Fail

Run 4 → Pass

Run 5 → Fail

 

Do not immediately add:

retries: 5

 

Retries can tell you that a problem is intermittent, but they do not explain why.

Common causes include:

  • Race conditions
  • Weak locators
  • Shared test state
  • Unstable test data
  • Network dependencies
  • Slow application rendering
  • Authentication state problems
  • Parallel execution
  • Environment differences

First capture evidence.

Use:

trace: 'on-first-retry'

 

Then inspect the failed run.

A good debugging question is:

“What changed between the passing run and the failing run?”

Debugging Tests That Fail Only in CI

This is one of the most important real-world Playwright debugging problems.

A test works on your laptop but fails in GitHub Actions or another CI environment.

Possible differences include:

  • Browser version
  • Operating system
  • Environment variables
  • Network speed
  • CPU resources
  • Test data
  • Authentication
  • Parallel workers
  • Time zone
  • Headless execution

Start with the CI test report and trace.

Playwright’s documentation recommends Trace Viewer for CI debugging because it provides a detailed view of the test execution rather than relying only on screenshots or videos.

For browser-launch failures, Playwright also supports the DEBUG environment variable. For example:

DEBUG=pw:browser npx playwright test

 

This can provide additional browser-launch debugging information.

Debugging Parallel Test Failures

Parallel testing can expose problems that never appear when tests run one at a time.

For example:

Test A creates customer “John”

Test B deletes customer “John”

Test A expects “John” to exist

 

If both tests run at the same time, the result may become unpredictable.

When investigating a suspicious failure, temporarily reduce concurrency and reproduce the problem.

The important question is:

Does the failure disappear when the test runs alone?

If yes, investigate:

  • Shared database records
  • Shared files
  • Shared accounts
  • Shared browser state
  • Test data collisions
  • Global application state

Do not assume that the browser is responsible.

The problem may be test isolation.

Debugging Browser Launch Errors

If Playwright cannot start the browser, the failure occurs before your actual test logic can run.

For example:

Error: Failed to launch browser

 

First check:

  • Browser installation
  • Playwright version
  • Operating-system dependencies
  • CI environment
  • Browser executable
  • Permissions

Use:

DEBUG=pw:browser npx playwright test

 

for additional browser-launch information.

A Real-World Debugging Example

Imagine this test:

test('user can complete checkout', async ({ page }) => {
  await page.goto('/shop');

  await page.getByRole('button', { name: 'Add to cart' }).click();

  await page.getByRole('link', { name: 'Checkout' }).click();

  await expect(page.getByRole('heading', { name: 'Checkout' }))
    .toBeVisible();
});

 

The test fails at:

await page.getByRole('link', { name: 'Checkout' }).click();

 

Bad debugging approach

You might write:

await page.waitForTimeout(5000);

 

Then try again.

It still fails.

Better debugging approach

Start:

npx playwright test --debug

 

Inspect the page.

You discover the application shows:

Cart (1)

 

but there is no Checkout link.

The cart drawer contains a button:

Proceed to checkout

 

The locator was wrong.

Fix:

await page
  .getByRole('button', { name: 'Proceed to checkout' })
  .click();

 

The problem was not timing.

It was the locator.

This is why good debugging starts with evidence instead of guesses.

The Debugging Decision Tree

When a Playwright test fails, use this simple decision tree.

Is there a locator error?

Check:

  • Element name
  • Role
  • Text
  • Frame
  • Visibility
  • Number of matches

Is there a timeout?

Check:

  • What Playwright was waiting for
  • Current URL
  • Application state
  • Network requests
  • Authentication
  • Locator

Is there an assertion failure?

Check:

  • Expected value
  • Actual value
  • Page state
  • Previous action

Does it fail only sometimes?

Check:

  • Race conditions
  • Test data
  • Shared state
  • Parallel execution
  • Network
  • Application rendering

Does it fail only in CI?

Check:

  • Trace
  • Browser/environment differences
  • CI logs
  • Test artifacts
  • Authentication
  • Environment variables

Does the browser fail to launch?

Check:

DEBUG=pw:browser npx playwright test

Playwright Debugging Best Practices

1. Read the error before changing code

The error message often contains the first useful clue.

2. Debug the root cause

Do not treat every timeout as a timing problem.

3. Prefer reliable locators

Use roles, labels, text, and explicit test IDs where appropriate.

4. Avoid unnecessary hard waits

This:

await page.waitForTimeout(5000);

 

should not become the default solution.

5. Use page.pause() for targeted debugging

It lets you stop exactly where you need to inspect the application.

6. Use Trace Viewer for difficult failures

Especially when the failure happened in CI.

7. Keep evidence

Screenshots, traces, videos, and logs can turn a mysterious failure into an understandable one.

8. Check the application, not only the test

The test may be correctly reporting an application problem.

9. Investigate flaky tests instead of hiding them

Retries can be useful, but they should not replace root-cause analysis.

10. Make tests independent

Parallel execution becomes much safer when tests do not depend on each other’s state.

Playwright Debugging Cheat Sheet

Situation

Try this first

Need live step-by-step debugging

npx playwright test --debug

Need to stop at one point

await page.pause()

Need source breakpoints

VS Code debugger

Need interactive test exploration

npx playwright test --ui

Locator does not work

Inspector / locator picker

CI failure

Trace Viewer

Need visual evidence

Screenshot

Need action sequence

Video

Need application values

console.log()

Need network evidence

Trace Network tab / network listeners

Browser launch failure

DEBUG=pw:browser

Intermittent failure

Trace + test isolation

Multiple matching elements

Inspect locator matches

iframe failure

frameLocator()

Popup failure

Listen for new page

Frequently Asked Questions About Playwright Debugging

How do I debug a Playwright test?

Run the test with:

npx playwright test --debug

 

Then use Playwright Inspector to pause, step through actions, inspect locators, and examine the browser state.

What does page.pause() do in Playwright?

page.pause() stops test execution at that exact point so you can inspect the browser and debug the next actions interactively.
await page.pause();

 

How do I debug Playwright tests in VS Code?

Install the Playwright VS Code extension, open the Testing sidebar, select the test, and run it in debug mode. You can use breakpoints and inspect the browser while the test executes.

What is the difference between Playwright Inspector and Trace Viewer?

Inspector is mainly for debugging a test while it is running.

Trace Viewer is mainly for investigating a recorded test execution after it has happened.

Trace Viewer can provide action details, DOM snapshots, screenshots, network requests, console information, and metadata.

How do I debug a Playwright timeout?

First identify what Playwright was waiting for.

Then check:

  • Locator
  • Visibility
  • URL
  • Authentication
  • iframe
  • Network
  • Application state

Do not automatically increase the timeout.

How to Diagnose and Fix Flaky Playwright Tests?

Capture a trace, reproduce the failure, compare passing and failing executions, and investigate timing, test isolation, data, network, and parallel execution.

How to Troubleshoot Playwright Failures in CI?

Use the Playwright HTML report and Trace Viewer. Configure traces such as:

use: {
  trace: 'on-first-retry',
}

 

Then inspect the trace from the failed CI run. Playwright specifically recommends traces as a powerful way to investigate CI failures.

Can I debug Playwright network failures?

Yes. Playwright provides APIs for monitoring and modifying network traffic, and Trace Viewer provides a Network tab for inspecting requests associated with test actions.

Final Thoughts

Playwright debugging is not about adding more waits until a test turns green.

It is about finding the real reason the test failed.

A strong debugging workflow looks like this:

Test fails

   ↓

Read the error

   ↓

Find the failed action

   ↓

Inspect the page state

   ↓

Check the locator

   ↓

Check timing and application state

   ↓

Inspect network/authentication/frames if needed

   ↓

Use Inspector, UI Mode, VS Code, or Trace Viewer

   ↓

Identify the root cause

   ↓

Fix the cause

   ↓

Run the test again

 

For local interactive debugging, start with Playwright Inspector, page.pause(), VS Code, or UI Mode.

For failures that are difficult to reproduce—especially in CI—use Trace Viewer.

And remember one of the most important Playwright debugging lessons:

A failed line tells you where the test noticed the problem.It does not always show where the problem began.

If you learn to separate those two things, debugging Playwright tests becomes much easier.

Playwright Masters automation testing logo - White Back ground

Playwright Masters Team

Playwright Automation Testing Experts | Industry-Focused Training & Practical Learning

Playwright Masters is a dedicated automation testing training platform focused on helping learners build practical skills in Playwright automation testing. Our training covers real-world automation concepts, framework practices, coding, debugging, API testing, cross-browser testing, CI/CD, and interview preparation to help learners develop job-ready testing skills.

Scroll to Top