skip to content

In Playwright, what does DEBUG=pw:api show you that a headed re-run does not?

level: seniorimportance: should knowfreq 37%

answer

  1. A logging switch, not an interactive one
  2. Names the element each locator resolved to
  3. One line per retry, with the reason
  4. Works where no window can open
  5. Scope it and redirect stderr

basics

~20 s

Playwright's DEBUG=pw:api logging prints its own call log to stderr: every API call, the element each locator resolved to, and the reason each action retried. A headed re-run shows the final picture but never names the failing check.

solid answer

~50 s

`DEBUG=pw:api npx playwright test` turns on Playwright's internal API logging. Every call is written to stderr with the locator it resolved, the checks the action is waiting on, and one line per retry - `element is not visible`, `element is outside of the viewport`, `intercepts pointer events`. That last category is what a headed re-run cannot give you: watching the browser shows a button that looks perfectly clickable, while the log names the invisible overlay sitting on top of it. Because it is only logging, it changes nothing about how the run executes - no headed window, no worker or timeout changes - so it works on a build agent or a container where no Inspector could ever open. The cost is volume: a full suite produces enormous output, so scope it to one spec and redirect it to a file.

code

bash · 8 lines
bash
# scope it to one spec and keep the log out of the reporter output
DEBUG=pw:api npx playwright test tests/driver-map.spec.ts 2> pw-api.log

# a single test, plus browser launch diagnostics
DEBUG=pw:api,pw:browser npx playwright test tests/driver-map.spec.ts:42

# then read just the retry reasons
grep -E 'retrying|intercepts|not visible|outside of the viewport' pw-api.log

go deeper

for a junior

Know that Playwright can print its own call log with the DEBUG environment variable, and that a timing-out action already prints a shorter version of that log inside the error message.

for a middle

Explain what the log contains - the resolved element, the checks being waited on, and a reason on every retry - and that it is logging only, so nothing about the run's behaviour changes.

for a senior

Show that you read the retry reasons to distinguish a wrong locator from a blocked one, and that this is the surface you use on a machine where no interactive window can be opened.

for a principal

Decide how much diagnostic evidence a run should emit by default, so most failures are explained from what the pipeline already produced rather than from someone reproducing them by hand.

`DEBUG` is the standard Node debug-namespace variable, and Playwright registers several namespaces on it. `pw:api` is the one that logs the public API: what your test asked for, what Playwright resolved, and what it did about it. ## What the log actually contains For each call you get the call itself, then the internal steps underneath it: - the locator being waited for, and the element it **resolved to** (with a snippet of the real markup); - the action being attempted, and the actionability checks it is waiting on; - one line per **retry**, each naming the reason the previous attempt did not proceed; - navigation and network lifecycle steps as the page moves between states. A timing-out click on a food-delivery order tracker reads like this: ``` pw:api waiting for getByRole('button', { name: 'Refresh' }) pw:api locator resolved to <button class="refresh">Refresh</button> pw:api attempting click action pw:api waiting for element to be visible, enabled and stable pw:api element is visible, enabled and stable pw:api scrolling into view if needed pw:api done scrolling pw:api <div class="driver-map-overlay"></div> intercepts pointer events pw:api retrying click action, attempt #2 ``` Nine lines, and the answer is in one of them: a map overlay is covering the button. The locator was right, the element was there, and it was even visible - none of which a screenshot would have told you. ## Why a headed re-run does not answer this Watching the browser gives you the **result** of Playwright's decisions, not the decisions: | Question | Headed re-run | `DEBUG=pw:api` | |---|---|---| | Did my locator match anything? | you infer it from the outcome | the log names the resolved element | | Which check is failing? | invisible - checks are internal | printed on every retry | | How many times did it retry? | not shown | one line per attempt | | Runs without a display? | no | yes, it is only logging | | Effect on the run's behaviour | headed rendering differs | none | A transparent overlay, a `pointer-events` rule, an element two pixels outside the viewport, an animation that never settles - all of these look fine on screen and all of them are named explicitly in the log. ## The same information without any variable You do not always need the variable. When an action times out, Playwright includes a **call log** in the error message itself - the same "waiting for", "resolved to", "retrying" lines for the failing action. Read that first. Reach for `DEBUG=pw:api` when you need the story of the calls *leading up to* the failure, or when the run does not fail at all and you want to see what it is really doing. UI mode covers the same ground interactively through the Log pane on each action, so the environment variable is mainly the answer when you cannot open a window: a container, an SSH session, or a pipeline log you are reading after the fact. ## Other namespaces, and how to scope it - `DEBUG=pw:api` - the public API and actionability log; the one you almost always want. - `DEBUG=pw:browser` - browser process launch and stdout/stderr, for "the browser will not start" problems. - `DEBUG=pw:protocol` - the raw driver protocol traffic, enormous, and mostly useful when filing a bug against Playwright itself. - `DEBUG=pw:*` - everything, which is more than any human reads. Two practical habits: 1. **Scope it.** One spec file, or one test by `file:line`. A whole suite of order-tracker tests emits tens of thousands of lines. 2. **Redirect it.** The log goes to stderr, so `2> pw-api.log` keeps it out of your reporter output and gives you something greppable. On Windows the variable is set with `set DEBUG=pw:api` in cmd, or `$env:DEBUG="pw:api"` in PowerShell, rather than the inline prefix that works in a POSIX shell. ## When to reach for it Use it when the failure is *about* Playwright's own decision-making: an action that retries and times out, a locator you are not sure is resolving to the element you meant, or a hang with no visible cause. Do not use it as a general-purpose "more output" switch - it says nothing about your application's own logic, and its volume drowns the reporter output that usually tells you what went wrong.

  • The call log says the element intercepts pointer events. What is your next move?
    Identify the intercepting element - the log prints its markup - and decide whether it is legitimate. A cookie banner or a loading overlay means the test is missing a step or a wait for it to clear. If it is a decorative layer that should never block clicks, that is an application bug the test has just found.
  • Why prefer the call log embedded in a timeout error over re-running with DEBUG=pw:api?
    Because it is already there. Playwright puts the failing action's call log into the error message, so the first read costs nothing and needs no reproduction. Reach for the variable when you need the calls before the failure, or when the run hangs or passes rather than failing.
  • Does DEBUG=pw:api change how the tests run?
    No. It only adds logging to stderr - no headed window, no worker change, no timeout change. That is precisely why it is usable on a build agent or in a container, where an Inspector or UI mode window is not an option.

saying these in an interview costs you the question

  • Confuses DEBUG=pw:api with PWDEBUG=1
  • Thinks the log opens a browser window or Inspector
  • Expects it to log the application's own console output
  • Turns it on for the whole suite and drowns in output
  • Assumes retry reasons are only visible with the variable set
  • Believes it slows or otherwise changes test execution