Free tools Windows power users keep installed
One-click scans. No signup required.
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Debug browser automation by identifying the failing layer before increasing log volume. Start with the framework’s error and action log, then add page-console and request diagnostics, headed inspection, or a trace only when the failure requires that evidence. Playwright and Puppeteer expose different tools, so the shortest path depends on whether the problem is in your test or Node script, page JavaScript, the browser process, or the network.
Use a layered debugging workflow
Every browser test crosses several boundaries. Your test runner or Node code issues an action; the automation library sends protocol commands; the browser executes them; page JavaScript and network requests determine what the user sees. A single undifferentiated log makes these failures harder to separate.
- Read the original failure. Capture the assertion, expected and received values, selector, timeout, URL, and complete call log.
- Reproduce with the smallest useful diagnostic. Use Playwright API logs or Puppeteer protocol logs for action ordering. Do not enable every stream by default.
- Observe the page. Run headed, slow operations, and forward browser-console messages when page behavior is suspect.
- Capture state for intermittent or CI-only failures. Use a Playwright trace on an intentional retry policy, or preserve Puppeteer’s targeted logs and artifacts.
- Inspect the responsible process. Debug Node code with the Node inspector and browser-launch failures with browser stdout and stderr.
Keep trace archives and verbose output private. They can contain URLs, request data, page text, tokens, or other project information.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →How do I debug a Playwright test?
Start with the assertion and call log
Read the failure from the outside in. The assertion tells you what was observed; the call log shows which locator or action was waiting, and often records the last successful step. Check whether the locator matched zero elements, multiple elements, or an element that was present but not actionable. A timeout may indicate a wrong selector, a page transition that never completed, a blocked request, or a browser-side exception—not necessarily a slow test.
#1 Best Overall
In VS Code, the Playwright extension lets you set breakpoints, step through a test, and inspect locators. Its “Show Browser” option displays the run and can highlight locator matches, including multiple matches.
Turn on focused Playwright API logs
Enable the pw:api channel for the action sequence:
DEBUG=pw:api npx playwright test
PowerShell:
$env:DEBUG="pw:api"
npx playwright test
Windows Command Prompt:
set DEBUG=pw:api
npx playwright test
The output is useful for seeing when a locator was resolved, when an action began waiting, and which navigation or assertion followed it. Remove the variable after diagnosis; leaving verbose logging enabled makes CI output noisy and can expose data that should not be retained.
Make a local failure visible
Launch headed and slow the actions enough to watch the state change:
Recommended Free Tools
import { test } from '@playwright/test';
test('inspect checkout', async ({ page }) => {
await page.goto('https://example.com');
// Configure headless: false and slowMo in your project/browser launch settings.
await page.getByRole('button', { name: 'Continue' }).click();
});
Playwright’s debugging workflow also supports PWDEBUG=console. This makes a playwright object available in browser developer tools while the run is paused for inspection. WebKit has an important caveat: opening WebKit Inspector during execution prevents the script from proceeding and resets preconfigured user-agent and device emulation.
How do I inspect a Playwright trace from CI?
Record on a deliberate retry
Playwright Test commonly records a trace on the first retry, preserving evidence for a failure without tracing every passing test. A representative configuration is:
Rank #2
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
trace: 'on-first-retry'
}
});
Tracing every test can be performance heavy and creates more artifacts to store and protect. Choose a policy that matches your failure rate and retention requirements rather than treating tracing as a default replacement for diagnosis.
Read the trace in the report or Trace Viewer
Open the generated HTML report or trace with Playwright’s Trace Viewer. Move action by action through the timeline and inspect the corresponding DOM snapshot, source location, console output, and network requests. Timeline filters let you correlate a failed click with a console exception or a request that failed immediately beforehand. The browser-hosted viewer is documented as loading the trace locally in the browser without transmitting it externally.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteKnow what context tracing omits
The lower-level browserContext.tracing API records browser operations and network activity, but it does not record test assertions. If you need assertion context, retry metadata, and the complete test failure, Playwright Test’s trace configuration is the more complete route.
const context = await browser.newContext();
await context.tracing.start({ screenshots: true, snapshots: true });
// ... browser operations ...
await context.tracing.stop({ path: 'trace.zip' });
How do I debug Puppeteer by layer?
Puppeteer’s debugging guidance separates three possible sources: server-side Node code, client-side page code, and the browser process itself. Instrument only the layer that is failing.
Forward browser-console messages
Page-side console.* output does not automatically appear in Node. Add a listener:
page.on('console', msg => {
console.log('PAGE LOG:', msg.type(), msg.text());
});
This tells you whether a page exception, warning, or diagnostic message occurred before the selector timeout or wrong visual state.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Watch the page in headed mode
Launch with headless: false; use slowMo when a race is easier to understand visually than from timestamps. Puppeteer can also launch with devtools: true when you need Chromium’s developer tools alongside the page.
const browser = await puppeteer.launch({
headless: false,
slowMo: 150,
devtools: true
});
Debug Node-side execution
Put a debugger statement in the server-side code and start Node with the inspector:
node --inspect-brk scripts/capture.js
Attach from Chrome or Chromium at chrome://inspect/#devices. This is the right path for an incorrect loop, swallowed promise, malformed selector variable, or branch that never reaches Puppeteer.
Capture browser-process output
If Chromium crashes or fails to launch, forward its stdout and stderr:
Rank #4
const browser = await puppeteer.launch({ dumpio: true });
dumpio helps distinguish a browser startup problem from a page-level problem. Check installation as well: the normal puppeteer package downloads a compatible Chrome during installation, while puppeteer-core is library-only. If package-manager policy blocked install scripts, manually install a browser with:
npx puppeteer browsers install
Inspect protocol failures selectively
For suspected protocol or launch issues, enable Puppeteer’s internal channels:
# macOS/Linux
NODE_DEBUG="puppeteer:*" node scripts/capture.js
# PowerShell
$env:NODE_DEBUG="puppeteer:*"
node scripts/capture.js
Puppeteer warns that this output may contain sensitive information. Redact it before sharing and avoid unrestricted retention. For unresolved asynchronous calls, inspect browser.debugInfo.pendingProtocolErrors; it can show pending protocol errors and the stack traces that triggered them.
Capture the evidence your symptom requires
| Need | Playwright | Puppeteer |
|---|---|---|
| API or action ordering | DEBUG=pw:api |
NODE_DEBUG="puppeteer:*" for internal channels |
| Browser-side console | Context console events and Trace Viewer filters | page.on('console', ...) forwarding to Node |
| Interactive inspection | VS Code extension, headed run, UI/debug console, browser DevTools | Headed run, devtools: true, Node inspector |
| CI failure replay | First-retry trace and Trace Viewer | Targeted logs plus browser and Node diagnostics |
| Main caution | Always-on traces can be performance heavy; context tracing omits assertions | Verbose protocol logs may contain sensitive data |
Common failures and precise fixes
“Timeout exceeded” while clicking or locating
- Enable
DEBUG=pw:api(Playwright) or page-console forwarding (Puppeteer) to see whether the page reached the expected state. - Run headed with a delay and verify the selector manually.
- Check for multiple matches, an iframe, a navigation race, or an overlay intercepting the action.
- Inspect failed requests and page exceptions; a missing API response can leave the UI permanently waiting.
The page looks wrong but the test reports no useful error
Forward console messages, capture a trace, and inspect the DOM snapshot at the failed action. A browser-side exception is separate from a Node exception; logging only the Node process misses it.
The browser never launches
- For Puppeteer, enable
dumpio: trueand verify that installation scripts downloaded Chrome. - If using
puppeteer-core, provide and validate an executable path or install a browser withnpx puppeteer browsers install. - For Playwright, inspect the framework’s launch error before changing page waits; a launch failure occurs before page code runs.
CI fails but local runs pass
Record a trace on the first retry in Playwright Test. Compare its metadata, action timing, console output, and network activity with a local run. For Puppeteer, preserve the failing run’s targeted console, protocol, and browser-process output, while checking differences in browser version, environment variables, permissions, and network access.
Logs are too large or reveal secrets
Turn off debug variables after reproducing, capture only the failing retry, and restrict artifact access. Scrub authorization headers, cookies, query strings, personal data, and page content before sharing logs or traces.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Performance, reliability, and retention choices
- Use the smallest signal first: framework error and call log, then API logging, then page events, then a trace or inspector.
- Prefer conditional artifacts: first-retry traces preserve CI evidence without imposing trace overhead on every test.
- Keep timestamps and correlation: store the test name, retry number, browser version, URL, and commit alongside artifacts so parallel jobs remain distinguishable.
- Separate streams: label Node, browser-console, network, and browser-process output rather than merging everything into one unsearchable file.
- Do not “fix” a race by adding arbitrary sleeps: use logs and snapshots to identify the missing readiness condition, then wait for the relevant selector, response, or state.
Or skip the browser setup
When the goal is a clean image or PDF rather than debugging an automation script, ScreenshotNeo provides a single screenshot API call. It accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
Example using the documented API format (see the ScreenshotNeo documentation):
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
There are 1,000 screenshots per month free with no card. Paid plans start at $5 for 3,000 screenshots; every feature is included on every plan. Create a free ScreenshotNeo account.
FAQ
Should I enable every debug option at once?
No. Begin with the failure record and add one evidence source that can answer the next question. Broad logging obscures ordering and increases the chance of exposing sensitive data.
Does a Playwright trace replace a screenshot?
No. A trace is a timeline of automation state, snapshots, logs, and requests; a screenshot is only one visual frame. Use the artifact that matches the question.
Why are page console messages absent from Puppeteer output?
Browser-page console calls run in Chromium, not in your Node process. Register a page.on('console', ...) listener to forward them explicitly.
Frequently Asked Questions
Can I use the same environment variable syntax on every operating system?
No. Use DEBUG=pw:api command on macOS/Linux, $env:DEBUG="pw:api" in PowerShell, and set DEBUG=pw:api in Windows Command Prompt; Puppeteer’s NODE_DEBUG variable follows the same shell differences.
What should I retain from a failed CI job?
Retain the smallest useful set: the failure and call log, selected console or browser-process output, and a first-retry trace when using Playwright. Apply access controls and redact credentials, cookies, authorization headers, and personal data.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

