Introduction: What Are Playwright Flaky Test Failures?
A flaky test is a test that sometimes passes and sometimes fails even though the application code and test code have not intentionally changed.
For example:
Run 1 → PASS
Run 2 → PASS
Run 3 → FAIL
Run 4 → PASS
This is different from a genuine product failure.
A deterministic product bug normally fails consistently under the same conditions. A flaky Playwright test often depends on timing, shared state, network conditions, dynamic data, or the test environment.
Common examples include:
- Element appears at different times.
- API response is delayed.
- Tests share the same user account.
- Parallel workers modify the same data.
- Authentication expires.
- CI machines are slower than developer machines.
- Animations or dynamic content change the page state.
This playwright test flaky failures fix tutorial explains how to identify and eliminate these causes instead of simply adding retries or increasing timeouts.
What Causes Flaky Tests in Playwright?
The most common causes are:
| Cause | Example |
| Synchronization | Test clicks before UI is ready |
| Weak locator | Dynamic CSS class changes |
| Dynamic data | Product order changes |
| Shared state | Two tests modify the same account |
| Authentication | Session expires |
| Network | API response is slow |
| Parallel execution | Workers conflict |
| CI environment | Different resources |
| Animations | Element moves during interaction |
| Test dependency | Test assumes another test ran first |
The most effective Playwright flaky test fix is usually to make the test deterministic.
Common Symptoms of Playwright Test Flakiness
You may notice:
Test passes locally but fails in CI.
or:
Timeout waiting for locator
or:
Expected: “10”
Received: “9”
Other warning signs include:
- Failure disappears when running in debug mode.
- Failure disappears after rerunning.
- Test fails only under parallel execution.
- Test fails only on a particular browser.
- Screenshot shows a partially loaded page.
- Authentication occasionally redirects to login.
- Test passes after adding waitForTimeout().
That last symptom is particularly important.
If adding a fixed sleep makes the test pass, you probably have a synchronization problem—not a timeout problem.
Locator and Synchronization Problems
Consider this test:
await page.locator(‘.submit-button’).click();
The CSS class may be generated dynamically.
A more reliable locator is:
await page.getByRole(‘button’, {
name: ‘Submit’
}).click();
Playwright’s locator actions automatically wait for required actionability conditions. This makes reliable locators an important part of preventing flaky tests.
Problem → Root Cause → Fix → Best Practice
Problem: Button sometimes cannot be clicked.
Root cause: The locator is fragile or the button is still changing state.
Fix:
const submit = page.getByRole(‘button’, {
name: ‘Submit’
});
await expect(submit).toBeVisible();
await expect(submit).toBeEnabled();
await submit.click();
Best practice: Prefer semantic locators such as getByRole() and getByLabel().
Playwright Auto-Waiting and Reliable Assertions
Playwright provides automatic waiting for many browser actions.
Instead of:
await page.waitForTimeout(3000);
await page.getByRole(‘button’, {
name: ‘Checkout’
}).click();
use:
const checkout = page.getByRole(‘button’, {
name: ‘Checkout’
});
await expect(checkout).toBeVisible();
await checkout.click();
Assertions retry until their condition is satisfied or the assertion timeout is reached.
This is one of the most important principles in Playwright Test Flakiness prevention:
Wait for a meaningful application condition instead of waiting for an arbitrary amount of time.
Avoiding Unnecessary waitForTimeout() Usage
Problem
await page.getByRole(‘button’, {
name: ‘Search’
}).click();
await page.waitForTimeout(5000);
await expect(
page.getByText(‘Laptop’)
).toBeVisible();
Root Cause
The test doesn’t know what condition means “search completed.”
Fix
await page.getByRole(‘button’, {
name: ‘Search’
}).click();
await expect(
page.getByText(‘Laptop’)
).toBeVisible();
Best Practice
Avoid:
waitForTimeout()
for normal application synchronization.
Use:
- Locator assertions
- waitForURL()
- Response/event synchronization when genuinely needed
- API setup
- Application-specific readiness indicators
Test Data and Playwright Test Isolation
Shared data is one of the biggest causes of intermittent failures.
Imagine two parallel tests:
Test A → Login as user@example.com
Test B → Login as user@example.com
Both modify the same shopping cart.
Test A expects:
Cart = 1 item
But Test B adds another item.
Now Test A sees:
Cart = 2 items
The failure is intermittent because execution order can change.
Better approach
Use independent users:
const users = {
userA: ‘qa-user-a@example.com’,
userB: ‘qa-user-b@example.com’
};
Or generate independent test data through an API.
Best Practice
Each test should control its own:
- User
- Cart
- Order
- Database records
- Files
- Authentication state
Playwright’s browser contexts provide strong browser-level isolation, but your application data must also be isolated.
Authentication and Session-Related Flakiness
Authentication failures often appear to be UI failures.
For example:
await page.goto(‘/dashboard’);
await expect(
page.getByText(‘Welcome’)
).toBeVisible();
Sometimes the dashboard appears. Sometimes the application redirects to /login.
Root Cause
The authentication state is missing or expired.
Fix
Use a controlled authentication setup and reusable storageState where appropriate:
await page.context().storageState({
path: ‘playwright/.auth/user.json’
});
Then configure the project:
use: {
storageState: ‘playwright/.auth/user.json’
}
Best Practice
Don’t make every test perform UI login if your goal is to test an already-authenticated workflow. Use a dedicated authentication setup and isolate credentials.
Network and API-Related Flaky Failures
A UI test can fail because the backend is slow.
For example:
await page.getByRole(‘button’, {
name: ‘Generate Report
}).click();
await expect(
page.getByText(‘Report ready’)
).toBeVisible();
If the API sometimes takes 2 seconds and sometimes 15 seconds, the test can become intermittent.
Better approach
Wait for the actual UI condition:
await expect(
page.getByText(‘Report ready’)
).toBeVisible({
timeout: 20_000
});
If the API is a test dependency, you can also prepare test data through Playwright’s API capabilities instead of navigating through several UI screens.
Important
A larger timeout is appropriate only when the slower behavior is legitimate. If the API normally takes 2 seconds but occasionally takes 60 seconds, the test timeout is not the real problem—the backend performance may be.
Parallel Execution and Shared-State Problems
Parallel execution makes flaky tests easier to expose.
Run tests with multiple workers:
npx playwright test –workers=4
If tests fail only with multiple workers, investigate shared state.
Common conflicts include:
Same user
Same database record
Same shopping cart
Same output file
Same server resource
Same test account
Fix
Use independent test data.
You can also temporarily run:
npx playwright test –workers=1
If the failure disappears, that is a useful diagnostic signal—not necessarily the final solution.
Using Retries Correctly in Playwright
Playwright supports retries:
import { defineConfig } from ‘@playwright/test’;
export default defineConfig({
retries: process.env.CI ? 2 : 0
});
Retries can be useful for:
- Collecting evidence about intermittent failures
- Preventing a single environmental glitch from failing a pipeline
- Identifying tests that need investigation
But retries are not a real flaky test fix.
If this happens:
Attempt 1 → FAIL
Attempt 2 → PASS
the test should be investigated.
Do not hide the problem with:
retries: 10
A retry can make a pipeline green while the underlying test remains unreliable.
Debugging Flaky Tests with Trace Viewer, Screenshots, and Videos
Configure failure artifacts:
import { defineConfig } from ‘@playwright/test’;
export default defineConfig({
use: {
screenshot: ‘only-on-failure’,
video: ‘retain-on-failure’,
trace: ‘retain-on-failure’
},
reporter: [[‘html’]]
});
Run:
npx playwright test
Then:
npx playwright show-report
Trace Viewer helps you inspect:
- Actions
- Locator resolution
- Screenshots
- Timing
- Network activity
- Page state
- Errors
You can also debug interactively:
await page.pause();
or:
npx playwright test –debug
For intermittent failures, traces are particularly useful because the failure may disappear when you rerun the test manually.
Fixing Flaky Tests in CI/CD
A common scenario is:
Developer machine → PASS
CI/CD → FAIL
Possible causes
- Slower CPU
- Limited memory
- Network latency
- Different browser version
- Missing environment variables
- Authentication problems
- Parallel resource contention
- Different test data
Practical approach
First collect:
Trace
Screenshot
Video
Console errors
Test logs
Then compare the CI failure with a local run.
Avoid immediately increasing:
timeout: 120_000
Instead determine whether the CI environment is:
- Missing dependencies
- Running too many workers
- Using incorrect credentials
- Connecting to a slow environment
- Sharing test data
For reproducibility, Docker can help standardize browser and OS-level dependencies.
Real-World Playwright Flaky Test Troubleshooting Examples
Example 1: Dynamic product list
Problem: Product test sometimes fails.
Root cause: The product API has not finished rendering.
Fix:
await expect(
page.getByRole(‘listitem’).filter({
hasText: ‘Laptop’
})
).toBeVisible();
Best practice: Wait for the product condition rather than sleeping.
Example 2: Animation causes click failure
Problem: Button occasionally isn’t clickable.
Root cause: Animation moves the element.
Fix: Wait for the expected stable state or disable nonessential animations in the test environment.
Best practice: Don’t use force: true as the default solution.
Example 3: Shared shopping cart
Problem: Cart count changes unexpectedly.
Root cause: Parallel tests use the same account.
Fix: Give each test an independent account or reset the cart through controlled test setup.
Best practice: Isolate application state, not just browser pages.
Example 4: Local passes, CI fails
Problem: Test intermittently fails in GitHub Actions.
Root cause: CI has different performance or test-data conditions.
Fix: Inspect the trace and test environment, then reproduce using the same browser and configuration.
Best practice: Make CI failures observable rather than simply increasing retries.
Playwright Flaky Test Prevention Best Practices
Use this checklist:
Locators
- Prefer getByRole().
- Use getByLabel() for form fields.
- Use stable test IDs where necessary.
- Avoid dynamic CSS classes.
Synchronization
- Trust Playwright auto-waiting.
- Use web-first assertions.
- Avoid arbitrary sleeps.
- Wait for meaningful application state.
Test isolation
- Use independent users.
- Don’t share carts.
- Don’t depend on test execution order.
- Generate unique data where necessary.
Authentication
- Use controlled authentication state.
- Don’t rely on expired sessions.
- Keep credentials secure.
Parallel execution
- Identify shared resources.
- Make tests worker-safe.
- Run with one worker as a diagnostic technique.
CI/CD
- Capture traces.
- Capture screenshots and videos.
- Standardize environments.
- Investigate failures before adding retries.
Playwright Flaky Test Interview Questions
1. What is a flaky test?
A flaky test produces inconsistent results without an intentional change to the test or application.
2. What causes Playwright test flakiness?
Common causes include synchronization problems, weak locators, shared state, dynamic data, authentication issues, network delays, and CI resource differences.
3. Does increasing the timeout fix flaky tests?
Not necessarily. It can hide synchronization or application problems.
4. Why should waitForTimeout() be avoided?
It waits for a fixed duration rather than an actual application condition, making tests slower and potentially flaky.
5. How do you debug flaky Playwright tests?
Use:
Trace Viewer
Screenshots
Videos
Logs
6. Why do tests fail only in parallel?
They may share application data or other resources.
7. Are retries a flaky test solution?
Retries are a mitigation and diagnostic tool, not a substitute for fixing the underlying cause.
FAQs: Playwright Test Flaky Failures Fix
What causes Playwright flaky tests?
The most common causes are timing problems, dynamic content, unreliable locators, shared test data, authentication state, network delays, parallel execution, and CI environment differences.
How do I fix flaky tests in Playwright?
Make tests deterministic by using reliable locators, auto-waiting, web-first assertions, isolated test data, independent authentication, and controlled environments.
Why does my Playwright test pass locally but fail in CI?
CI may have different CPU, memory, network, browser, credentials, test data, or parallel-execution conditions.
Should I increase Playwright timeouts to fix flaky tests?
Only when the application operation is legitimately slow. Increasing every timeout can hide the real problem.
Does Playwright auto-wait?
Yes. Playwright automatically waits for many locator actions to become actionable, reducing the need for manual synchronization.
How do I identify a flaky test?
Run the test repeatedly and compare failures. If the same test alternates between passing and failing without relevant changes, investigate it as a potential flaky test.
