Test retries can make a CI pipeline less noisy, but they can also quietly train a team to ignore weak signals. The goal is not to eliminate red builds at any cost. It is to separate intermittent infrastructure noise from real product failures, keep feedback fast, and create a path to remove flakiness over time. This guide explains when retries help, when they cause damage, how many retries are usually reasonable, and how to wire them into automated testing for developers without hiding important failures.
Overview
If your team runs browser tests, API checks, integration suites, or full end-to-end testing in CI/CD testing pipelines, you have probably seen a job fail and then pass on the next run. That is the moment when retries enter the conversation.
Used carefully, retries are a practical control for transient failures: a delayed network response, a temporary browser startup issue, an environment timeout, or a dependency that was briefly unavailable. Used carelessly, retries become a blanket that covers poor waits, shared state, race conditions, and genuine regressions.
A good retry policy has three traits:
- It is narrow. Not every test gets the same treatment.
- It is observable. A pass-after-retry is tracked as a signal, not celebrated as success.
- It is temporary. Retries buy time to fix instability; they do not replace fixing it.
That distinction matters in reliable test automation. A test that passes on the second attempt is not equivalent to a test that passed on the first. From an engineering perspective, those are different outcomes and should be reported differently.
Before changing any configuration, align on one principle: retries should reduce random CI noise without lowering the team’s sensitivity to real failures. If a retry policy does not support that principle, it needs revision.
Retries also should not be your first move when a suite feels slow or unstable. Sometimes the better fix is structural: move more checks lower in the test pyramid, isolate test data, improve selectors, or speed up the suite with parallelism and sharding. If that is your current bottleneck, it is worth reviewing How to Set Up a Fast Feedback Test Pyramid for Web Applications and How to Speed Up Test Suites: Parallelization, Sharding, and Smart Caching.
Core framework
Here is a practical framework for adding retries for flaky tests without masking bugs. Think of it as a decision tree your team can revisit whenever failure patterns change.
1. Classify the failure before you classify the retry
Not all failures deserve the same response. Start by grouping failures into broad categories:
- Product failures: the application behavior is wrong.
- Test design failures: brittle selectors, fixed sleeps, hidden dependencies, poor cleanup.
- Environment failures: browser launch instability, temporary network issues, infrastructure contention, external service hiccups.
Retries are most defensible for environment failures, occasionally acceptable as a temporary shield for known flaky test design issues, and usually inappropriate for likely product failures.
2. Separate retry scope
One of the most common mistakes in CI pipeline for tests is applying one retry rule everywhere. Instead, choose the smallest scope that solves the problem:
- Assertion-level waiting: best for expected UI or API eventual consistency.
- Step or operation retry: useful for idempotent setup or polling tasks.
- Test-level retry: acceptable for intermittently unstable end-to-end paths.
- Job-level retry: reserve for runner crashes, environment provisioning failures, or known CI platform instability.
In many cases, the right answer is not a test retry at all. For example, if the application needs a moment for an element to become visible, use framework-native waiting rather than rerunning the entire test. Modern live testing tools such as Playwright are often strongest when you lean on built-in waiting and locator behavior instead of layering retries on top of fragile timing assumptions. If your team is comparing framework behavior, see Selenium vs Playwright: Which Browser Automation Tool Is Better Now?.
3. Treat first-pass success as the primary quality signal
A healthy suite should be judged first by how often tests pass on the first attempt. This metric is usually more honest than final pipeline status.
Track at least these outcomes:
- Passed first run
- Passed after retry
- Failed after all retries
- Infrastructure aborted
This approach keeps retries visible. It also helps you spot patterns: a test that almost always passes on retry is not healthy, even if the build is often green.
If you need better visibility for this, invest in reporting before increasing retries. A stronger report often solves the policy debate because it shows exactly where instability lives. Related reading: Best Test Reporting Tools for CI/CD Pipelines.
4. Set a retry budget
Teams often ask, “How many retries?” A useful evergreen answer is: fewer than you think, and only where the cost is justified.
In most workflows, one or two retries are enough to distinguish transient noise from repeatable failure. Beyond that, you usually start paying too much in these areas:
- slower feedback loops
- higher compute cost
- more confusing reports
- more hidden regressions
- weaker trust in CI
A practical default for retries for flaky tests looks like this:
- Unit tests: usually no retries; fix determinism instead.
- API and integration tests: limited retries only for clearly transient dependency issues.
- End-to-end tests: one retry for selected suites, sometimes two for especially noisy remote environments.
- Deployment smoke checks: one targeted retry can be reasonable if startup timing is variable, but a persistent failure should stop the release.
If your team keeps raising retry counts, take that as a warning. You are probably compensating for architecture or process debt, not improving CI/CD testing.
5. Gate on intent, not on convenience
Different pipelines can tolerate different retry rules. Your pull request checks, nightly regression jobs, pre-release validations, and post-deploy smoke tests should not necessarily behave the same way.
A simple model:
- Pull request pipeline: keep retries low and feedback fast.
- Main branch regression suite: allow slightly more tolerance, but report retry-heavy tests prominently.
- Release validation: be stricter for critical flows and business-risk paths.
- Post-deploy smoke test pipeline: allow targeted retries for startup race conditions, not for core business assertions.
This is especially helpful when deciding between smoke tests, sanity tests, and broader regression coverage. If your gates are blurred, revisit Smoke Tests vs Sanity Tests vs Regression Tests: When to Use Each.
6. Create an expiry path for flaky tests
A retry policy without ownership becomes permanent. Every flaky test that receives retries should have:
- a reason it was granted retries
- an owner or team
- a review date
- a linked issue or investigation note
This turns retries into operational debt with a due date rather than a silent default.
7. Debug before expanding the policy
If a test benefits from a retry, gather evidence before you widen the rule to similar tests. Capture traces, screenshots, logs, video, timing data, and network details. The goal is to identify whether the failure is caused by app timing, selector design, state leakage, environment instability, or external dependencies. A strong debugging workflow is often what prevents retries from becoming a crutch. For browser suites, see How to Debug Failed Browser Tests in CI with Videos, Traces, and Screenshots and How to Reduce Flaky Tests in CI: A Practical Troubleshooting Checklist.
Practical examples
The principles are easier to apply when tied to common CI situations. Here are several realistic examples and the logic behind them.
Example 1: Browser test fails because a button is not yet actionable
A checkout test intermittently fails when clicking a button that appears visually present but is not yet ready for interaction.
Bad fix: add two test retries and move on.
Better fix: replace fixed sleeps with framework-native waiting, use a more stable locator, and assert readiness before the click.
Why: this is a test design issue, not a transient infrastructure problem. Retrying the whole test hides the actual flaw.
If you are building with Playwright tutorial-style patterns, this is often where locator strategy and built-in waiting outperform manual timing.
Example 2: A remote browser occasionally fails to launch in CI
The same end-to-end test suite passes locally and usually passes in CI, but one or two runs per week fail before any test steps execute due to browser startup or worker initialization issues.
Reasonable approach: allow a limited job-level or test-level retry, mark these failures separately, and monitor runner health.
Why: the failure happens outside business assertions and may be environmental. Still, if frequency grows, the right fix is runner stability or resource tuning, not more retries.
Example 3: API test hits occasional rate limits from a shared nonproduction service
An API suite fails sporadically because a shared environment is overloaded.
Reasonable approach: retry the specific idempotent request or test once, with clear logging that rate limiting occurred.
Also do this: reduce environment contention, isolate test accounts, or improve service quotas.
Why: this is a candidate for narrowly scoped retry, but the long-term fix belongs in environment design. For related patterns, see API Testing in CI/CD: Best Tools, Pipeline Patterns, and Failure Checks.
Example 4: Visual regression checks differ because fonts load inconsistently
A visual snapshot test sometimes fails because rendering inputs vary slightly across runs.
Bad fix: keep rerunning until the diff disappears.
Better fix: standardize rendering conditions, control fonts and animations, and use retries only if the capture step is known to be unstable for a temporary reason.
Why: visual tests require environmental consistency more than brute-force retries. See Visual Regression Testing Tools: Playwright, Percy, Loki, and Applitools Compared.
Example 5: A flaky test passes on the second run 40 percent of the time
This is exactly the kind of pattern that can poison CI trust.
Recommended policy: keep the single retry if it prevents daily disruption, but flag the test as flaky, remove it from critical gating if risk allows, assign ownership, and set a deadline for cleanup.
Why: the retry is only a short-term workflow control. The pass-after-retry rate is telling you that the test is unreliable.
Example 6: Release smoke tests fail on a new deployment because a dependent service is still warming up
After deployment, a smoke test occasionally fails within the first minute, then passes shortly after.
Reasonable approach: use a short, explicit retry or polling window for readiness checks before running the business assertion.
Why: this is not the same as retrying a failed checkout flow three times. You are waiting for a system to become ready, which is often better modeled as a readiness probe than as a generic retry.
Across all of these examples, the key question is the same: what are we learning from the retry? If the answer is “nothing,” the policy is probably too blunt.
Common mistakes
Most retry problems are not caused by the feature itself. They come from weak policy and weak reporting.
Using retries to compensate for fixed sleeps
If your suite relies on arbitrary delays, retries will not make it reliable. They only stack more waiting on top of fragile timing. Prefer explicit conditions, stable events, and framework-supported waits.
Applying the same retry count to every suite
Unit tests, API checks, browser tests, and deployment validations have different failure modes. A uniform retry count is convenient to configure but usually poor engineering.
Counting pass-after-retry as a full success
This is the most damaging reporting mistake. A green build can still contain serious instability. Keep pass-after-retry visible in dashboards and discussions.
Adding retries without collecting artifacts
When a retry makes a failure disappear, teams lose the easiest opportunity to diagnose it. Always capture enough evidence on the first failure attempt.
Retrying known bad assertions
If a test fails because expected business behavior changed, repeating it adds noise and delays feedback. Product failures should stay loud.
Ignoring the cost of retries in parallel environments
Retries increase total workload. In heavily parallel suites, that can create more queueing, contention, and timing variance, which then creates even more flaky failures. This can become a feedback loop.
Leaving retries in place forever
A temporary exception becomes policy surprisingly quickly. Review retry-heavy tests regularly and remove the allowance when the underlying cause is fixed.
When to revisit
Your retry policy should be treated as a living part of your automated QA workflow. Revisit it whenever the underlying conditions change, especially when the primary method changes or when new tools and standards appear.
Use this practical checklist to decide when an update is due:
- Your framework changed: for example, you moved from one browser automation tool to another, or adopted newer built-in waiting behavior.
- Your CI platform changed: different runners, containers, caching, or resource limits can alter failure patterns.
- Your suite became more parallel: retries interact with sharding, worker isolation, and shared resources.
- Your failure mix changed: more infrastructure failures may require environment work rather than test policy changes.
- Your reporting improved: once you can identify pass-after-retry patterns clearly, you may be able to tighten the policy.
- Your release risk changed: a critical business flow may need stricter gating than it did before.
A simple quarterly review works well for many teams. During that review, ask:
- Which tests consumed the most retries?
- Which retries protected developer flow, and which ones only hid instability?
- Are retries concentrated in one suite, one service, one environment, or one team boundary?
- Can we replace test-level retries with better waits, setup isolation, or readiness checks?
- Should any flaky tests be downgraded, quarantined, or rewritten?
If you want a practical baseline, start with this action plan:
- Set retries to zero by default for deterministic low-level tests.
- Allow one retry for selected end-to-end tests that are known to have intermittent environment noise.
- Track first-pass rate and pass-after-retry rate separately.
- Capture artifacts on the first failure attempt.
- Require an owner and review date for any test with special retry treatment.
- Review retry-heavy tests every sprint or month until the count drops.
The healthiest teams do not ask whether retries are good or bad in the abstract. They ask whether retries are helping the pipeline tell the truth. If the answer is yes, and the usage is visible, limited, and temporary, retries can be a responsible part of CI retries without masking bugs. If the answer is no, they are only making unreliable test automation easier to ignore.