Isolated Retries — Minimize Interference
Playwright 1.62 adds retryStrategy: 'isolated' — run all retries at the end in a single worker to minimize interference with the rest of the suite. Perfect for flaky-test forensics.
📖 The Story: The Retry That Poisoned the Suite
A team had a flaky test that failed ~10% of the time. When it failed, Playwright retried it immediately in the next available worker. But the retry sometimes failed too — not because the test was broken, but because the failed first attempt had left a database connection open, and the retry’s queries timed out waiting for the lock.
The team tried increasing retries to 3, but that made it worse — each retry interfered with the next, and the suite’s overall pass rate dropped because the flaky test’s retries were poisoning other tests sharing the database.
They tried isolating the flaky test in its own project, but that required maintaining a separate config and a deny-list of “do not run with other tests” files.
retryStrategy: 'isolated' in 1.62 fixes this. Failed tests retry at the end of the suite, one by one in a single worker, so they can’t interfere with other tests.
🎯 Why It Matters
No Interference
Retries run at the end in a single worker. Failed first attempts can’t poison other tests.
Forensics
Perfect for nightly flaky-test hunting — see if a test fails consistently when isolated.
Clean State
Each retry gets a fresh worker. No leftover state from the previous attempt’s failure.
Deterministic
Retries run sequentially, not in parallel — easier to reason about and debug.
🌍 Real World Example
A team’s nightly flaky-test hunt runs 500 tests with 3 retries. With the default 'immediate' strategy, a flaky test’s retries run as soon as a worker is free, often overlapping with other tests that share a database. With retryStrategy: 'isolated', all 500 tests run first (no retries), then any failures retry one-by-one at the end in a single worker. The flaky test’s retries can’t interfere with other tests, and the team gets a clean signal: “this test failed 3/3 times when isolated — it’s genuinely broken, not environmentally flaky.”
🧒 Explain Like I'm 10
Imagine you’re taking a test in a classroom. If you fail a question, the teacher lets you retry it immediately — but the kid next to you is also retrying, and you keep bumping elbows. You both fail again because you’re distracting each other.
Now, the teacher says: “everyone finish the test first. If anyone failed a question, they can retry it at the end, alone in the room.” No more bumping elbows — each retry happens in isolation, so you either pass on your own merits or fail because the question is genuinely too hard.
🎓 Professional Explanation
Playwright’s default retry strategy is 'immediate' — when a test fails, it retries as soon as a worker is free. This is fast (retries overlap with other tests) but can cause interference: a failed first attempt may leave shared state (database connections, file locks, environment variables) that poisons the retry or neighboring tests.
Playwright 1.62 adds retryStrategy: 'isolated'. With this strategy, all tests run first without retries. After the suite completes, any failed tests retry one-by-one in a single worker. Each retry gets a fresh worker, eliminating interference from the previous attempt’s failure. This is slower (retries are sequential at the end) but produces cleaner signals for flaky-test forensics.
📊 The Flow
Immediate vs Isolated Retry Strategy
⚙️ retryStrategy: 'isolated'
Set retryStrategy in your playwright.config.ts. The default is 'immediate'; set it to 'isolated' for flaky-test forensics.
class=class="tk-str">"tk-com">// playwright.config.ts import { defineConfig } from class="tk-str">'@playwright/test'; export default defineConfig({ retries: 2, retryStrategy: class="tk-str">'isolated', class=class="tk-str">"tk-com">// new in 1.62 });
With 'isolated', all tests run first without retries. After the suite completes, any failed tests retry one-by-one in a single worker. Each retry gets a fresh worker, eliminating interference.
class=class="tk-str">"tk-com">// Per-project strategy: immediate for PRs, isolated for nightly export default defineConfig({ projects: [ { name: class="tk-str">'pr', testMatch: class="tk-str">'**/*.spec.ts', retries: 1, retryStrategy: class="tk-str">'immediate', class=class="tk-str">"tk-com">// fast feedback for PRs }, { name: class="tk-str">'nightly', testMatch: class="tk-str">'**/*.spec.ts', retries: 3, retryStrategy: class="tk-str">'isolated', class=class="tk-str">"tk-com">// clean signal for flaky hunting }, ], });
Use different strategies per project. PRs want fast feedback (immediate), nightly flaky-hunts want clean signals (isolated). This is the recommended setup for teams that take flakiness seriously.
class=class="tk-str">"tk-com">// CLI override class=class="tk-str">"tk-com"># Run with isolated retries for one-off flaky hunting npx playwright test --retries=3 --retry-strategy=isolated class=class="tk-str">"tk-com"># Or via env var in CI RETRY_STRATEGY=isolated npx playwright test
You can override the config via CLI (--retry-strategy=isolated) or environment variable. Useful for one-off flaky-test hunts without changing the config file.
🔄 Before vs After
Before: Immediate retries (default, all versions)
export default defineConfig({ retries: 2, class=class="tk-str">"tk-com">// retryStrategy defaults to class="tk-str">'immediate' }); class=class="tk-str">"tk-com">// Test fails → retries ASAP in next free worker class=class="tk-str">"tk-com">// Retry may overlap with other tests sharing a database class=class="tk-str">"tk-com">// Failed first attempt's state may poison the retry
Immediate retries are fast but can cause interference. A failed first attempt may leave shared state (DB connections, file locks) that poisons the retry or neighboring tests.
After: Isolated retries (1.62+)
import { defineConfig } from class="tk-str">'@playwright/test'; export default defineConfig({ retries: 2, retryStrategy: class="tk-str">'isolated', }); class=class="tk-str">"tk-com">// All tests run first (no retries) class=class="tk-str">"tk-com">// Failed tests retry at end, one-by-one in a single worker class=class="tk-str">"tk-com">// Each retry gets a fresh worker → no interference
Isolated retries are slower (sequential at the end) but produce clean signals. Each retry gets a fresh worker, so a failed first attempt can’t poison the retry. Perfect for flaky-test forensics.
Isolated retries extend total suite wall-clock time. With 'immediate', retries overlap with other tests. With 'isolated', retries run sequentially at the end. For a suite that takes 10 minutes with immediate retries, isolated retries can push that to 12–15 minutes if multiple tests fail. Reserve isolated retries for nightly flaky-hunt jobs; keep immediate for per-PR CI gates.
⚠️ Common Mistakes
Mistake 1: Using isolated retries for PR CI
Isolated retries are slower because they run sequentially at the end. For PR CI, developers want fast feedback — use the default 'immediate' strategy. Reserve 'isolated' for nightly flaky-hunt jobs.
Mistake 2: Expecting isolated retries to fix flaky tests
Isolated retries give you a cleaner signal about whether a test is genuinely flaky, but they don’t fix the underlying flakiness. If a test fails 3/3 times with isolated retries, it’s genuinely broken — fix the test or the code, don’t just add more retries.
Mistake 3: Not setting retries high enough for isolated
With isolated retries, you want enough attempts to distinguish “flaky” from “broken.” A test that fails 1/3 times is flaky; a test that fails 3/3 times is broken. Use retries: 3 minimum for isolated strategy so you get this signal.
✅ Best Practices
- Use
'immediate'for PR CI — fast feedback for developers. - Use
'isolated'for nightly flaky-hunts — clean signal, no interference. - Set
retries: 3minimum for isolated to distinguish flaky from broken. - Don’t use isolated to “fix” flakiness — it gives signal, not a cure.
- Use per-project strategies to serve both PRs and nightly from one config.
🏗️ Senior Engineer Deep Dive
Why isolated retries produce cleaner signals
With immediate retries, a failed first attempt may leave shared state: an open database transaction, a file lock, a modified environment variable, a cached value in a service. The retry runs in a fresh worker, but the shared state persists across workers. With isolated retries, all tests complete first (so the shared environment is back to baseline), then each retry runs in a single worker with a fresh setup. If a test fails 3/3 times in isolation, you know it’s the test that’s broken, not environmental interference.
Combining isolated retries with sharding
In a sharded CI run, each shard is independent — isolated retries within a shard don’t affect other shards. This means you can run isolated retries on a known-flaky shard without slowing down the rest of the suite. Configure the flaky shard’s project with retryStrategy: 'isolated' and the stable shards with 'immediate'. The stable shards finish fast; the flaky shard takes longer but produces a clean signal.
💼 Interview Questions
Show Answer
Show Answer
Show Answer
Show Answer
🏋️ Practical Exercise
Compare Immediate vs Isolated
- Pick a known-flaky test in your suite
- Run it 3 times with immediate retries:
npx playwright test flaky.spec --retries=3 - Note the pass/fail pattern and total time
- Now run with isolated retries:
npx playwright test flaky.spec --retries=3 --retry-strategy=isolated - Compare the pass/fail pattern and total time
- If the test passes with isolated but fails with immediate, it’s environmentally flaky (shared state issue)
🚀 Mini Project
🏗️ The Nightly Flaky Dashboard
- Create a separate Playwright project called “nightly” with
retryStrategy: 'isolated'andretries: 3 - Run it nightly via GitHub Actions cron:
0 4 * * * - Write a custom reporter that logs each test’s retry outcome (e.g., “3/3 failed”, “2/3 passed”)
- After a week, identify tests that failed 3/3 more than once — these are genuinely broken
- Identify tests that passed 2/3+ — these are environmentally flaky
- File issues for both categories with the retry data as evidence
📝 Quiz
Test your understanding. Click an option to check your answer.
❓ FAQ
Q: Does retryStrategy work with sharding?
A: Yes. Each shard is independent — retryStrategy applies within each shard. You can run isolated retries on a known-flaky shard without affecting other shards. This is useful for isolating flaky tests in their own shard with isolated retries while keeping stable shards on immediate.
Q: Can I see which retry attempt failed in the report?
A: Yes. The HTML reporter shows each retry attempt as a separate entry with its status. With isolated retries, you’ll see all first attempts grouped together, then all retry attempts at the end. Custom reporters can access testInfo.retry to log which attempt failed.
Q: Does isolated retries affect the test timeout?
A: No. The test timeout (testConfig.timeout) applies to each attempt individually. Isolated retries just change when retries happen, not how long each one can run. A 30-second timeout still applies to each of the 3 retry attempts.
📦 Summary
🎯 Key Takeaways
- retryStrategy: 'isolated' runs all tests first, then retries failures one-by-one at the end.
- Use for nightly flaky-hunts — clean signal, no interference from previous failures.
- Use 'immediate' (default) for PR CI — fast feedback for developers.
- Set retries: 3 minimum for isolated to distinguish flaky (1/3) from broken (3/3).
- Per-project strategies let you serve PRs and nightly from one config.
Comments
Comments
Post a Comment