Skip to content

Cypress Flaky Tests: Retries, Detection & Fixes (2026)

Cypress tests flake from fixed cy.wait() calls, animations, and cross-test state. Fix with intercept-and-wait, isolation, and retries, then track flakiness.

İbrahim Süren
Founder · Jun 25, 2026 · 8 min read
Cypress Flaky Tests: Retries, Detection & Fixes (2026)
Get Qualflare updates

Product news and testing tips.

Cypress tests flake mostly from fixed cy.wait(ms) calls, animations, and state leaking between tests. Cypress queries and assertions already auto-retry, so the fixes are: wait on network aliases instead of time, rely on retry-ability instead of arbitrary waits, keep test isolation on, and enable test retries — then track which tests still flake across runs.

Key takeaways

  • Cypress queries and assertions already auto-retry — flakiness usually comes from bypassing that with cy.wait(time).
  • Wait on an intercepted network alias (cy.intercept + cy.wait('@alias')), not a fixed duration.
  • Keep test isolation enabled so state doesn't leak between tests.
  • Enable Cypress test retries in run mode to absorb residual flakiness.
  • Retries pass over the symptom; track flake history to find and fix the cause.

Cypress fights flakiness by design — queries and assertions automatically retry until they pass or time out. So flaky Cypress tests almost always trace back to something that defeats that retry-ability. This guide covers the usual culprits and their fixes. For the framework-agnostic view, see the complete guide to flaky tests. Where we reference Qualflare, we describe only what it actually does.

Why Cypress tests flake

Cypress’s retry-ability means most timing is handled for you — until you bypass it. The recurring causes, especially under the slower, busier conditions of CI versus local:

  • Fixed cy.wait(2000) waits that guess at timing and lose the race on a slow runner.
  • Animations and transitions that briefly delay elements.
  • State leaking between tests — leftover cookies, local storage, or server data.

All three introduce non-determinism — the outcome depends on timing the test doesn’t control.

The fixes

Replace cy.wait(time) with network aliases

The biggest win. Instead of guessing a duration, wait for the actual request:

// Flaky: guesses how long the request takes
cy.wait(2000);
cy.get('.results').should('be.visible');

// Stable: waits for the response, however long it takes
cy.intercept('GET', '/api/results').as('getResults');
cy.get('[data-test="search"]').click();
cy.wait('@getResults');
cy.get('.results').should('be.visible');

This is both faster (no over-waiting) and stable across environments.

Lean on retry-ability, not arbitrary waits

cy.get(...).should(...) retries the query and assertion together until they pass. Assert on the end state you expect rather than inserting waits before it — the assertion is the wait.

Keep test isolation on

Modern Cypress enables test isolation by default, resetting state between tests so one test’s leftovers can’t flake the next. Keep it on; if you disable it for a suite, you take on the burden of cleaning state yourself.

Handle animations

Elements mid-animation can be actionable-but-unstable. Cypress’s actionability checks catch most of this; for stubborn cases, disable CSS animations/transitions in the test environment.

Enable test retries

For residual flakiness, configure retries so CI stays unblocked:

// cypress.config.js
module.exports = defineConfig({
  retries: { runMode: 2, openMode: 0 },
});

Retries in runMode (CI) absorb intermittent failures without retrying during local development.

Detect flakiness over time

Flaky tests are common at scale — Google found almost 16% of its tests exhibit some flakiness (Google Testing Blog, 2016). Retries keep the build green, but a test that only passes on retry is still a flaky test — and the free Cypress runner won’t tell you which tests flake across runs over time. For that you need history-based detection: export Cypress results as JUnit or mochawesome and send them to an observability layer that scores reliability. See Cypress test reporting. Qualflare’s flaky test detection scores each test’s flakiness from its history and clusters related failures, so you fix causes instead of chasing symptoms. Want to size the problem first? The flaky test cost calculator turns your CI volume and flake rate into a monthly dollar cost.

Start free with Qualflare — send your Cypress results and get flaky scoring and a reliability trend, free to start.

Flaky tests in other frameworks: Playwright, pytest, and Jest & Vitest.

Flaky Cypress tests in CI and parallel runs

The causes above get worse, not better, once specs move off your laptop and into CI — especially once they run in parallel.

cypress run behaves differently than cypress open

cypress open renders in a visible browser you can watch and pause on; cypress run — what CI actually executes — runs headless by default, in a smaller default viewport (1000x660 unless overridden), with no interactive time-travel debugger. A test that depends on an element being visible at a particular viewport size, or that happens to pass locally because you’re watching it run slowly enough for an animation to finish, can flake for the first time the moment it hits cypress run in CI. See why tests pass locally but fail in CI for the general version of this problem. Fix: run cypress run locally at least occasionally, so headless-specific failures surface before CI finds them for you.

Parallel runs multiply shared-state problems

Test isolation stops one test polluting the next within a single worker. It does nothing about state shared outside Cypress — a seeded database row, a rate-limited API key, a fixture file two specs both write to. Run those specs sequentially on one machine and the collision might never happen, or happen rarely enough to ignore. Split the same suite across four parallel CI jobs, and now four workers hit that same seed data or rate limit at once — a flake that occurred once a month starts occurring on every third build. This is the single most common way a suite that “was fine” starts flaking right after a team adopts CI parallelization.

The fix isn’t disabling parallelization — it’s making shared resources actually shared-safe: give each parallel worker its own database schema or namespaced test data (a worker-ID-suffixed prefix works for most seeding strategies), and treat any external API a test hits as something that needs its own quota or a stub, not a shared account everyone’s tests compete for.

cy.session() caching behaves differently per worker

Cypress’s cy.session() caches login/auth state across tests in the same run to skip re-authenticating before every test — a real speed win. But that cache lives in-memory, per Cypress process, so each parallel worker gets its own copy. If your session setup itself is flaky — a login endpoint that’s occasionally slow, a token that expires mid-run — you’ll see it fail inconsistently across workers rather than consistently everywhere, which looks like a Cypress “sometimes” flake but is actually a login flow with its own reliability problem wearing a disguise. Validate session setup separately from the tests that consume it, and give it the same cy.intercept-based waiting treatment covered above instead of a bare cy.wait(time).

Splitting specs across a CI matrix without Cypress Cloud

Cypress’s own --record --parallel balances spec files across machines automatically, but it requires a Cypress Cloud account. Without Cloud, you can still split a spec glob across a CI matrix by hand, or with a package like cypress-split that divides specs by matrix index without any external service:

# .github/workflows/e2e.yml
strategy:
  fail-fast: false
  matrix:
    containers: [1, 2, 3, 4]
steps:
  - uses: actions/checkout@v4
  - run: npm ci
  - run: npx cypress run
    env:
      SPLIT: 4
      SPLIT_INDEX: ${{ matrix.containers }}

Whichever splitting method you use, it only distributes wall-clock time — it doesn’t fix the shared-state problem above. If anything, more parallel workers means more chances for two specs to collide on the same seed data at once, so fix shared-state isolation before you add shards, not after.

For the framework-agnostic treatment of shard-count math, dynamic vs. static splitting, and CI-matrix YAML for GitHub Actions, GitLab, and CircleCI, see test parallelization and sharding.

Frequently asked questions

Why are my Cypress tests flaky?

The most common causes are fixed cy.wait(milliseconds) calls that guess at timing, animations that delay elements, and state leaking between tests. Cypress queries and assertions already auto-retry, so replacing time-based waits with network-alias waits and keeping test isolation on removes most flakiness.

How do I stop using cy.wait with a fixed time?

Intercept the request and wait on its alias: cy.intercept(‘GET’, ‘/api/x’).as(‘getX’), trigger the action, then cy.wait(‘@getX’). This waits exactly until the response arrives instead of guessing a duration, which is both faster and stable across environments.

Does Cypress have built-in test retries?

Yes. You can configure retries in cypress.config (for example retries: { runMode: 2, openMode: 0 }), so a failing test is retried in CI. Treat retries as a way to stay unblocked while you fix root causes, not as a permanent fix — a test that only passes on retry is still flaky.

How do I detect which Cypress tests are flaky?

You need history across runs, not a single result. Export Cypress results (JUnit or mochawesome) and send them to a test observability tool that scores each test’s flakiness from its pass/fail history, so you can see and prioritize the worst offenders.

Does running Cypress tests in parallel cause new flakiness?

Not directly, but it multiplies flakiness that was already latent in shared state. Tests that occasionally collide on the same seeded database row or rate-limited API key rarely collide when run sequentially on one machine — split across four parallel CI jobs, four workers can hit that same resource at once, turning a once-a-month flake into a once-every-few-builds one. Give each worker its own namespaced test data instead of disabling parallelization.

Ready to ship with confidence?

Start free with Qualflare's AI-powered test management.