Playwright & Web Automation Hub

Playwright architecture, selector reliability, and advanced interaction patterns.

Capturing Screenshots and Video on Test Failure

When a test fails on CI you were not watching, the only way to understand it is the evidence the run left behind. Playwright can record a screenshot, a video, and a full trace for every test, but recording all three on every run is slow and fills disk, so the practical pattern is to capture them only when a test fails. Three use options — screenshot: 'only-on-failure', video: 'retain-on-failure', and trace: 'on-first-retry' — give you rich forensic artifacts at almost zero cost on the happy path. This page belongs to Reporters & Test Artifacts, part of the Debugging & Test Observability guide, and walks through configuring and attaching these artifacts.

Conditional artifact capture by test outcome A test outcome branching to no artifacts on pass and to screenshot, video, and trace on failure or first retry. Test runs Pass no artifacts Fail / retry capture all screenshot video + trace
A passing test leaves nothing behind; a failing or retried test produces a screenshot, video, and trace, keeping the happy path fast and disk usage low.

The three artifact options

All three are set under use in playwright.config.ts, and each accepts a mode that ties capture to the outcome:

The configuration

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

export default defineConfig({
  // Retries are needed for trace: 'on-first-retry' to ever trigger.
  retries: process.env.CI ? 2 : 0,
  use: {
    // Save one image only when a test fails.
    screenshot: 'only-on-failure',
    // Record every test but keep the video only for failures.
    video: 'retain-on-failure',
    // Record a full trace on the first retry of a failed test.
    trace: 'on-first-retry',
  },
  // The HTML reporter attaches all captured artifacts automatically.
  reporter: [['html', { open: 'never' }]],
});

Because trace: 'on-first-retry' only fires when a test is retried, it does nothing unless retries is above zero — the retry and timeout settings in Configuring Retries and Timeouts for Stable CI are a prerequisite.

Attaching custom artifacts

Beyond the automatic captures, you can attach your own files — a downloaded export, an API response, a manual screenshot — to the report with testInfo.attach(). They appear inline in the HTML report next to the test:

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

test('attaches a manual screenshot to the report', async ({ page }, testInfo) => {
  await page.goto('/dashboard');
  // Capture a buffer rather than writing to disk first.
  const shot = await page.screenshot();
  // Attach it so it shows up inline in the HTML report for this test.
  await testInfo.attach('dashboard-state', { body: shot, contentType: 'image/png' });
  await expect(page.getByRole('heading', { name: 'Overview' })).toBeVisible();
});

Step-by-step setup

  1. Enable retries so traces can trigger. Set retries: process.env.CI ? 2 : 0, because trace: 'on-first-retry' records nothing unless a failed test is actually retried.
  2. Set screenshot to only-on-failure. Add screenshot: 'only-on-failure' under use so a full-page image is saved for failures and nothing is written for passing tests.
  3. Set video to retain-on-failure. Add video: 'retain-on-failure' so every test is recorded but only failure videos are kept, giving you the full sequence that led to the break without storing thousands of green recordings.
  4. Set trace to on-first-retry. Add trace: 'on-first-retry' so the second attempt of a failing test produces a complete replayable trace, the single most useful artifact for diagnosis.
  5. Use the HTML reporter to surface artifacts. Configure ['html', { open: 'never' }] so all captured screenshots, videos, and traces attach to their tests automatically and the report does not pop open in CI.
  6. Attach any custom evidence with testInfo.attach(). For files Playwright does not capture by default, call await testInfo.attach(name, { body, contentType }) inside the test so the artifact appears inline in the report.

Verification

Confirm capture works three ways. First, deliberately fail a test locally and check the test-results folder — a screenshot and video should appear for that test and for no passing test. Second, open the HTML report and verify the failed test shows its image, video, and a trace launcher inline. Third, force a retry on CI and confirm the trace from the second attempt downloads and opens in the Trace Viewer & Debugging guide. Finally, publish these artifacts from your pipeline as described in CI/CD Integration so every failed build links to its evidence.

Frequently Asked Questions

Why is no trace captured even though I set trace on-first-retry?

The on-first-retry mode only records a trace when a failed test is retried, so it never fires if retries is zero. Set retries above zero in your config — commonly process.env.CI ? 2 : 0 — or switch to trace: 'retain-on-failure' if you want a trace on the very first failure without a retry.

Does video: 'retain-on-failure' slow down passing tests?

Playwright records every test to produce the failure videos, so there is a small overhead even on passing runs, but the recording is discarded the moment a test passes. The cost is modest and usually worth the evidence; if it matters for a huge suite, scope video capture to a single project rather than the whole run.

How do I attach a file Playwright does not capture automatically?

Use testInfo.attach(name, { body, contentType }) inside the test, passing a buffer or a path and the correct MIME type. The attachment appears inline in the HTML report next to that test, which is the way to surface downloaded exports, API payloads, or manual screenshots alongside the automatic artifacts.

Back to overview