Introduction: What Are Playwright Test Retries?
If a Playwright test fails intermittently, retries can run the test again automatically. This feature is useful for identifying flaky tests, especially in CI/CD environments.
For example, you can configure:
import { defineConfig } from ‘@playwright/test’;
export default defineConfig({
retries: 2
});
With this configuration, Playwright can retry a failed test up to two additional times.
However, developers sometimes report that playwright test retries not working even though retries is configured.
Usually, the problem is not the retry engine itself. Common causes include:
- The wrong configuration file is being loaded.
- The test is actually passing.
- Retries are configured for the wrong project.
- A CLI or environment configuration changes behavior.
- The developer expects repeatEach to behave like retries.
- The test is being run outside Playwright Test.
- The failure is happening during setup in a way the developer does not expect.
- The developer is checking the wrong report or result.
This playwright test retries not working tutorial explains how retry behavior works and how to troubleshoot it.
What Are Retries in Playwright?
Retries tell the Playwright Test Runner to rerun a failed test.
For example:
import { test, expect } from ‘@playwright/test’;
test(‘login test’, async ({ page }) => {
await page.goto(‘https://example.com’);
await expect(
page.getByRole(‘heading’, { name: ‘Example Domain’ })
).toBeVisible();
});
With:
retries: 2
Playwright can attempt the failed test again.
Retries are particularly useful for detecting tests that:
- Fail because of timing
- Depend on unstable external services
- Have race conditions
- Are sensitive to CI resource availability
- Occasionally encounter transient network problems
But retries should not be used to hide unstable automation.
A test that passes only after several retries still needs investigation.
How Playwright Retry Behavior Works
Suppose you configure:
retries: 2
The conceptual flow is:
Attempt 1
↓
FAIL
↓
Attempt 2
↓
FAIL
↓
Attempt 3
↓
PASS
The test has ultimately passed, but its retry history indicates instability.
Playwright exposes retry information through testInfo.retry.
Configuring Retries Globally in playwright.config.ts
The simplest configuration is:
import { defineConfig } from ‘@playwright/test’;
export default defineConfig({
testDir: ‘./tests’,
retries: 2,
use: {
headless: true
}
});
Then run:
npx playwright test
If a test fails and retry conditions apply, Playwright will retry it according to the configured policy.
Verification
Run a deliberately failing test:
import { test, expect } from ‘@playwright/test’;
test(‘retry demonstration’, async () => {
expect(false).toBe(true);
});
Then:
npx playwright test
You should see retry attempts in the test output.
Configuring Retries for a Specific Project
Large frameworks often have multiple projects.
For example:
import { defineConfig } from ‘@playwright/test’;
export default defineConfig({
projects: [
{
name: ‘chromium’,
use: {
browserName: ‘chromium’
},
retries: 2
},
{
name: ‘firefox’,
use: {
browserName: ‘firefox’
},
retries: 1
}
]
});
This lets you control retry behavior independently.
Important troubleshooting point
If you configure retries on one project but execute another project, you may conclude that Playwright test retries are not working.
Check which project you are actually running.
For example:
npx playwright test –project=chromium
Test-Level Retry Configuration
If you want special behavior for a particular group of tests, use a project or configuration strategy rather than assuming every test-level API works like global configuration.
A common approach is to use separate projects for different reliability policies.
This keeps retry rules explicit and easier to maintain.
Using testInfo.retry
testInfo.retry tells you which retry attempt is currently executing.
Example:
import { test } from ‘@playwright/test’;
test(‘inspect retry attempt’, async ({}, testInfo) => {
console.log(‘Retry number:’, testInfo.retry);
});
The first attempt reports:
Retry number: 0
A subsequent retry reports:
Retry number: 1
Another retry:
Retry number: 2
This is useful when debugging flaky tests.
For example:
import { test } from ‘@playwright/test’;
test(‘retry debugging’, async ({ page }, testInfo) => {
console.log(`Running attempt ${testInfo.retry + 1}`);
await page.goto(‘https://example.com’);
});
Difference Between Retries, repeatEach, and Workers
These concepts are often confused.
| Feature | Purpose |
| retries | Reruns failed tests |
| repeatEach | Runs tests multiple times intentionally |
| workers | Controls parallel test execution |
For example:
export default defineConfig({
retries: 2,
repeatEach: 3,
workers: 4
});
These settings do different jobs.
Retries
Used when a test fails.
Repeat Each
Used when you deliberately want to run a test multiple times.
Workers
Control parallel execution.
Therefore, changing:
workers: 1
does not enable retries.
Why Are Playwright Test Retries Not Working?
1. Wrong Configuration File
Problem: You added:
retries: 2
but nothing changes.
Cause: The command may be loading another configuration file.
Fix:
Explicitly specify the configuration:
npx playwright test –config=playwright.config.ts
Best Practice: Keep one clear primary configuration file unless multiple configurations are intentional.
2. The Test Is Passing
Retries happen after failures.
If:
test(‘stable test’, async () => {
expect(1).toBe(1);
});
passes on the first attempt, there is nothing to retry.
Do not expect Playwright to rerun passed tests because retries is enabled.
Use repeatEach when repeated execution is the actual requirement.
3. Running the Wrong Project
Problem: Retry configuration exists but does not seem effective.
Cause: The test is running under another project.
Check:
npx playwright test –project=chromium
Then verify the chromium project contains the intended retry configuration.
4. Expecting Retries to Fix Every Failure
Retries are a safety mechanism, not a repair mechanism.
If a test consistently fails:
Attempt 1 → FAIL
Attempt 2 → FAIL
Attempt 3 → FAIL
the test is probably genuinely broken.
If it behaves like:
Attempt 1 → FAIL
Attempt 2 → PASS
it may be flaky.
That distinction is critical for debugging.
Retries in Local Development vs CI/CD
A common best practice is to use fewer retries locally and more in CI.
For example:
import { defineConfig } from ‘@playwright/test’;
export default defineConfig({
retries: process.env.CI ? 2 : 0
});
This means:
- Local execution → no retries
- CI execution → two retries
Why?
Local retries can hide problems during development.
CI retries can help identify intermittent failures caused by:
- Network latency
- Resource contention
- Browser startup timing
- External services
But a retry should still generate evidence for investigation.
Debugging Flaky Tests With Traces and Screenshots
Configure traces:
import { defineConfig } from ‘@playwright/test’;
export default defineConfig({
retries: 2,
use: {
trace: ‘on-first-retry’,
screenshot: ‘only-on-failure’,
video: ‘retain-on-failure’
}
});
This is particularly useful because you can investigate the failed attempt rather than only seeing the final result.
You can open a trace with:
npx playwright show-trace trace.zip
Screenshots can reveal:
- Unexpected popups
- Missing elements
- Loading states
- Authentication failures
- Layout problems
Videos can reveal:
- Timing issues
- Animations
- Navigation
- Unexpected redirects
Real-World Playwright Retry Example
Problem → Cause → Configuration/Fix → Verification → Best Practice
Problem
A dashboard test occasionally fails because the API response is slow.
Test
import { test, expect } from ‘@playwright/test’;
test(‘dashboard loads’, async ({ page }, testInfo) => {
console.log(`Attempt: ${testInfo.retry + 1}`);
await page.goto(‘https://example.com/dashboard’);
await expect(
page.getByTestId(‘dashboard’)
).toBeVisible();
});
Configuration
import { defineConfig } from ‘@playwright/test’;
export default defineConfig({
retries: 2,
use: {
trace: ‘on-first-retry’,
screenshot: ‘only-on-failure’
}
});
Verification
If the output indicates:
Attempt 1 → FAIL
Attempt 2 → PASS
you have evidence of intermittent behavior.
Best Practice
Investigate why the API occasionally responds slowly instead of permanently relying on retries.
Handling Retries With Parallel Execution
Parallel execution can expose resource-related flakiness.
For example:
npx playwright test –workers=8
may behave differently from:
npx playwright test –workers=1
If failures disappear with one worker, investigate:
- CPU usage
- Memory
- Database contention
- Shared test data
- API rate limits
- Port conflicts
- Shared files
Retries may make the symptoms less visible without fixing the underlying parallelization problem.
Common Configuration Mistakes and Solutions
| Problem | Cause | Solution |
| Retries never run | Test passes | Test failure is required |
| Wrong retry count | Wrong config | Verify loaded configuration |
| Retry works locally but not CI | Different config | Check CI command/environment |
| Project retry ignored | Wrong project | Run intended project |
| Passed tests don’t repeat | Misunderstanding retries | Use repeatEach |
| Flaky test remains | Underlying instability | Fix synchronization |
| Retry hides failure | Too many retries | Keep retry count reasonable |
| No debugging evidence | Missing artifacts | Enable trace/screenshots/video |
Playwright Retry Best Practices
Follow these guidelines:
- Keep retries low.
- Use retries mainly for CI resilience.
- Do not use retries to hide flaky tests.
- Investigate tests that pass only after retry.
- Use testInfo.retry for diagnostics.
- Capture traces on retry.
- Capture screenshots for failures.
- Use HTML reports to identify flaky tests.
- Keep test data isolated.
- Avoid shared state between tests.
- Make API and UI synchronization deterministic.
- Check worker/resource interactions.
- Keep CI configuration explicit.
A strong Playwright framework treats retries as a signal about reliability, not as a substitute for stable automation.
Playwright Test Retries Interview Questions With Answers
How do you enable retries in Playwright?
Configure the retries property:
export default defineConfig({
retries: 2
});
Why are Playwright retries not working?
Check whether the correct configuration file and project are being used, whether the test actually fails, and whether CLI/CI configuration changes the expected behavior.
What does testInfo.retry do?
It identifies the current retry number. The first attempt is 0, the first retry is 1, and so on.
What is the difference between retries and repeatEach?
Retries rerun failed tests. repeatEach intentionally runs a test multiple times regardless of whether previous executions passed.
Should retries be used to fix flaky tests?
No. They can provide temporary resilience and diagnostic evidence, but the underlying flakiness should still be fixed.
Why use retries in CI?
CI environments can expose intermittent timing, network, and resource issues. A small retry count can reduce false-negative pipeline failures while the underlying cause is investigated.
FAQs
How do I enable retries in Playwright?
Add a retries setting to playwright.config.ts:
export default defineConfig({
retries: 2
});
Then run your tests normally with npx playwright test.
Why are Playwright test retries not working?
Verify the correct configuration file, project, CI environment, and test command. Also remember that retries only occur after a test failure.
Does Playwright retry passed tests?
No. Retries are for failed tests. Use repeatEach when you intentionally want repeated execution.
How many retries should Playwright tests use?
A small number such as one or two is generally more useful than a large retry count. Excessive retries can make CI slower and hide genuine problems.
How can I debug a Playwright test after retry?
Use testInfo.retry, traces, screenshots, videos, and HTML reports to compare the original failure with the retry attempt.
Do Playwright retries work with parallel execution?
Yes. Retries can occur in a parallel test environment, but shared resources and test-data conflicts can cause additional instability.
