100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Testing

Retries and Flake Reduction

Learn how Cypress's built-in retry-ability and test-level retries work, and practical techniques for reducing flaky end-to-end tests.

CI & ScalingIntermediate10 min readJul 10, 2026
Analogies

Retry-ability: Cypress's Core Anti-Flake Mechanism

Most Cypress commands (like .get(), .find(), .click()) and nearly all assertions are automatically retried internally against the default defaultCommandTimeout (4 seconds) before failing — this is called retry-ability, and it's the primary reason well-written Cypress tests are far less flaky than raw Selenium scripts with manual sleep() calls. When you write cy.get('.spinner').should('not.exist'), Cypress doesn't check once and fail; it re-queries the DOM repeatedly until the assertion passes or the timeout elapses, automatically absorbing the natural delay of an async UI update like a loading spinner disappearing after an API call resolves.

🏏

Cricket analogy: This is like a third umpire re-checking a run-out replay from multiple camera angles and re-examining until a conclusive decision emerges, rather than issuing a verdict off a single blurry frame — retry-ability keeps re-checking the DOM until a conclusive state is reached.

Test-Level Retries with `retries` Configuration

Beyond command-level retry-ability, Cypress supports whole-test retries via the retries config option, which reruns an entire failed test up to a specified number of times before marking it as truly failed — configurable separately for runMode (headless CI) and openMode (interactive), since retries are far more useful in CI than during active local development. A test marked as passing on retry is flagged in Cypress Cloud as 'flaky' rather than simply 'passed,' which is a critical distinction: teams that only look at pass/fail counts can miss a test that's failing 30% of the time and being silently rescued by retries, masking a real underlying stability problem.

🏏

Cricket analogy: This is like a bowler getting a no-ball called back and re-bowling the delivery — the over eventually counts as complete, but the scorer still logs the no-ball separately rather than pretending it never happened.

javascript
// cypress.config.js
module.exports = {
  e2e: {
    retries: {
      runMode: 2,   // retry up to 2 times in CI
      openMode: 0,  // no retries during local interactive dev
    },
    defaultCommandTimeout: 8000,
  },
};

Cypress Cloud's Analytics tab tracks flaky-test trends over time per spec. A test that passes only on retry across many recent runs should be treated as a bug report, not ignored — chasing down its root cause (usually a race condition or an unstable selector) is almost always cheaper long-term than tolerating repeated retries.

Common Root Causes of Flake and How to Fix Them

The most common flake source is asserting against network-dependent state without waiting on the actual request: clicking a 'Save' button and immediately asserting a success toast appears, without first using cy.intercept() and cy.wait('@saveRequest') to guarantee the API call has actually completed, races the UI update against real network latency that varies run-to-run. A second frequent cause is animation timing — clicking an element mid-transition (a modal still sliding into place) can hit the wrong coordinates; Cypress's built-in waitForAnimations setting mitigates this by waiting for CSS transitions to settle before acting. A third cause is test interdependence, where test B silently relies on state left behind by test A (like a created record); this should be replaced with independent setup via cy.request() or a database seed in beforeEach, since Cypress explicitly recommends tests be able to run in isolation.

🏏

Cricket analogy: This is like a fielder throwing to the keeper for a run-out before confirming the ball has actually left the outfielder's hand — timing the throw off assumption rather than confirmed possession leads to fumbles; waiting for cy.wait('@saveRequest') is the equivalent of confirming the ball is truly in hand before the throw.

Avoid cy.wait(3000) as a flake fix. Fixed waits either slow down every run by the full duration (even when the condition resolves instantly) or aren't long enough under CI load and start failing intermittently again. Always prefer waiting on a specific condition — a network alias, an element's state, or an assertion — over an arbitrary timer.

  • Retry-ability automatically re-runs most Cypress commands and assertions against the command timeout before failing, absorbing normal async UI delays.
  • The retries config option reruns a whole failed test up to N times, configurable separately for runMode and openMode.
  • A test that only passes on retry is flagged 'flaky' in Cypress Cloud — a signal to investigate, not ignore.
  • The most common flake cause is asserting UI state without waiting on the underlying network request via cy.intercept()/cy.wait().
  • Animation timing and test interdependence (shared state between tests) are two other frequent flake sources.
  • cy.wait(fixedMilliseconds) is an anti-pattern; wait on specific conditions (aliases, element state) instead.
  • Tests should be independent and self-seed their required state, rather than relying on execution order.

Practice what you learned

Was this page helpful?

Topics covered

#Testing#CypressStudyNotes#TestingQA#RetriesAndFlakeReduction#Retries#Flake#Reduction#Retry#StudyNotes#SkillVeris