How to Set Screenshot Capture Retries for a Flaky Webpage with Playwright
Learn when to retry a Playwright test, extend a visual assertion timeout, or set a direct screenshot timeout—and how to stabilize flaky captures.
Direct answer: Playwright has three separate controls that can help with flaky screenshot workflows. Set retries to rerun an entire failed Playwright Test; set the toHaveScreenshot assertion timeout to give visual stabilization more time; or set page.screenshot({ timeout }) to limit one direct capture operation. A direct screenshot timeout is not a retry count.
For a flaky visual check, first wait for a meaningful page state and adjust the visual assertion timeout only if the page needs more time to settle. Add whole-test retries only when intermittent test failures remain. Retries can make a suite more resilient, but they do not prove a screenshot is correct.
Choose the right kind of retry
| Control | What it repeats or limits | Set it in | Use it when |
|---|---|---|---|
retries |
Reruns the entire failed test | Playwright Test config or CLI | A test sometimes fails for transient reasons and a rerun is useful for resilience and diagnosis |
toHaveScreenshot({ timeout }) |
Gives the visual assertion more time to obtain two consecutive matching screenshots and compare with the baseline | Assertion call or shared expect.timeout |
The page is settling slowly, but should become stable within a bounded period |
page.screenshot({ timeout }) |
Sets the timeout for one direct screenshot operation | Screenshot call | You use page.screenshot() directly and need to bound that operation |
Playwright Test defaults to no whole-test retries. The documented default test timeout is 30 seconds and the default assertion timeout is 5 seconds. Action and navigation timeouts have no default limit. The visual assertion retry window is governed by the expect timeout unless overridden; the direct screenshot operation has its own timeout, documented as defaulting to zero. These controls are independent, so change the one associated with the failing operation.
Give a visual screenshot assertion more time
toHaveScreenshot is already a stabilization assertion: it captures repeatedly until two consecutive screenshots match, then compares the result with the expected baseline. Set its timeout when that stabilization period needs a larger window.
import { test, expect } from '@playwright/test';
test('dashboard screenshot is stable', async ({ page }) => {
await page.goto('/dashboard');
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
await expect(page).toHaveScreenshot({ timeout: 15_000 });
});
The heading assertion waits for an application condition before visual comparison. Replace it with a locator that represents the state your screenshot requires. Tune the timeout to your application and CI environment; increasing it cannot fix a page whose content is inherently nondeterministic.
Set a shared assertion timeout
If many visual assertions need a different default window, set expect.timeout in the Playwright configuration. A per-assertion timeout can still make an exceptional case explicit.
import { defineConfig } from '@playwright/test';
export default defineConfig({
expect: {
timeout: 10_000,
},
});
Choose a value based on observed application behavior and CI conditions. Avoid raising it across the suite without evidence: longer assertion windows can make genuine failures take longer to report.
Retry the entire failed test
Configure retries in playwright.config.ts when rerunning a complete test is appropriate. The retry starts the test again; it does not rerun only the screenshot call.
import { defineConfig } from '@playwright/test';
export default defineConfig({
retries: 2,
});
The equivalent command-line setting is:
npx playwright test --retries=2
Playwright labels a test as flaky when the first attempt fails but a retry passes. If the initial run and all retries fail, the test remains failed. Treat that signal as evidence to investigate instability, not as proof that the resulting screenshot is correct.
Set a timeout for a direct screenshot
If you call page.screenshot() outside toHaveScreenshot, set its operation timeout independently. This does not rerun the capture.
import { test } from '@playwright/test';
test('save a page screenshot', async ({ page }) => {
await page.goto('/dashboard');
await page.screenshot({
path: 'page.png',
timeout: 10_000,
});
});
Use a finite value when you need the capture operation bounded. If it times out, diagnose why the capture cannot complete; adding test retries would only rerun the whole test.
Stabilize the page before comparing pixels
Retries are most useful after the test has a clear readiness condition. Prefer web-first assertions and locator checks over arbitrary sleeps. Playwright documentation cautions that “Tests that wait for time are inherently flaky.”
- Wait for the relevant UI: assert that the heading, table, chart, or other required locator is visible or has the expected state.
- Control changing content: hide or normalize timestamps, rotating content, or other areas that are expected to vary and are not part of the comparison. Screenshot APIs support styling for repeatable captures.
- Account for animations: use screenshot assertion animation controls when motion is causing unwanted differences, and verify the behavior against your installed Playwright version.
- Keep environments consistent: use a consistent operating system, browser version, settings, hardware, power source, and headless mode for baselines and comparisons. Playwright documents these as sources of rendering variation.
- Inspect external dependencies: network-dependent UI and font loading can make the page differ between attempts. Wait for the application state the test actually needs, and investigate the source of variation.
Do not increase snapshot tolerance or retry count until you have considered whether the difference is a genuine visual regression. The right fix depends on whether the capture is slow, the assertion is unstable, or the image is actually changing.
Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| The screenshot call has no retry option | page.screenshot() controls one capture; whole-test retries belong to Playwright Test. |
Use config or --retries to rerun a test, or use toHaveScreenshot for visual stabilization. |
toHaveScreenshot times out while the page is still settling |
The assertion window is too short, or the page has no reliable readiness condition. | Wait for a meaningful locator state first; then adjust the per-assertion timeout or shared expect.timeout. |
| Increasing timeout does not make the test stable | The rendered page may be nondeterministic, for example because content changes between captures. | Identify and control the changing content; keep the rendering environment consistent. |
| Direct screenshot operation times out | The capture operation exceeded its own configured timeout. | Investigate the capture and page state, then set a suitable page.screenshot({ timeout }). Do not confuse it with retries. |
| The test passes on retry but fails on its first attempt | The test is intermittent; the retry masks the first failure for the suite result but does not remove the underlying cause. | Review the failed attempt and stabilize its readiness conditions or environment. |
| Baseline differs only in CI | Rendering can vary by host OS, browser version, settings, hardware, power source, or headless mode. | Generate and compare baselines in a consistent environment. |
Performance, reliability, and cost
A visual assertion may take multiple captures to establish that consecutive screenshots match. Extending its timeout can increase the time spent waiting on a failing or unstable assertion. Whole-test retries rerun setup and test actions too, so they can increase suite duration and repeat any side effects in the test. Keep tests safe to rerun and choose retry counts deliberately.
Fixed sleeps waste time when the page is ready early and still fail when it is ready late. A meaningful condition improves both reliability and runtime. In CI, consistent browser and host environments reduce rendering variation; retries do not eliminate environment differences.
Playwright’s cited documentation does not provide a numeric measure of retry effectiveness. Do not assume a particular retry count will fix flakiness; use failed attempts as diagnostic evidence.
Or skip the browser setup
If your task is to capture a webpage rather than test its behavior in a browser, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns an image or PDF. See the ScreenshotNeo API docs for configuration.
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}`);
ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each cleanup step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server offers take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Every feature is on every plan.
Sign up for 1,000 free screenshots a month, with no card required.
FAQ
Does Playwright retry just the screenshot when a test fails?
No. The Playwright Test retries setting reruns the whole failed test. toHaveScreenshot separately captures until two consecutive screenshots match.
Which timeout should I increase first?
Use the timeout for the operation that is failing. For visual stabilization, set the assertion timeout. For a direct capture operation, set the screenshot timeout. For slow test actions or navigation, inspect their corresponding timeout settings.
Can retries make a visual test trustworthy?
Retries can reveal intermittent failures and help a suite continue through transient issues, but a retry passing does not establish that the screenshot is deterministic or visually correct.
Do timeout options vary by Playwright version?
Some screenshot assertion options are version-specific. Check the API documentation against the version installed in your project before relying on a newer option.


