Run Web Automation Tests at Scale with Agentic AI
Generate tests with AI agents or automate test suites with Selenium, Playwright and other frameworks. Run tests in parallel on TestMu AI scalable cloud grid.
- TestMu AI (Formerly LambdaTest)
- /
- Blog
- /
- Cypress Flaky Tests: Causes, Retries and Fixes (2026)
Cypress Flaky Tests: Causes, Retries and Fixes (2026)
Cypress flaky tests come from fixed waits, late intercepts and broken retry-ability. Learn how Cypress retries work, reproduce the flake and fix it with code.
Published on:
Cypress tests flake mostly from fixed cy.wait(ms) calls, network intercepts registered too late, and commands that break retry-ability. Cypress doesn't retry failed tests by default; set retries: { runMode: 2, openMode: 0 } in cypress.config. A test that passes on retry is flaky: log it, give it an owner, and fix the cause.
This guide covers Cypress 16.2.1, the latest release on npm at the time of writing (published 28 October 2026). TestMu AI (formerly LambdaTest) runs Cypress suites as they are, so every fix here works in the setup you already have. New to the framework? Start with the Cypress tutorial, and for the wider picture on causes and policy, see the flaky test guide.
Why Are Cypress Tests Flaky?
Cypress tests flake when a command runs before the app is ready. The usual causes are fixed waits, intercepts registered after the request, and assertions that run once instead of retrying.
The Cypress documentation lists the race conditions behind most unreliable tests: animations, API calls, test server or database availability, resource dependencies and network issues, all covered in its test retries guide. Patterns in your own test code turn those races into failures.
Fixed cy.wait(ms) Calls
A fixed wait is a guess about how long the app takes. Guess too short and the test fails on a slow run. Guess too long and every run pays for it. The Cypress best practices mark waiting for arbitrary time periods as an anti-pattern and recommend route aliases or assertions instead.
Intercepts Registered Late
An intercept only sees requests sent after it is registered. When the page fires a request while loading and cy.intercept() comes after cy.visit(), the request has already gone. The test then times out on cy.wait('@alias'), or passes only when the request happens to be slow. The Cypress intercept guide covers the full API.
Commands That Break Retry-ability
Queries and assertions retry. Actions and .then() callbacks run once. A check placed inside .then() sees whatever the DOM holds at that instant, so it passes on a fast run and fails on a slow one.
Slow Backends and Shared CI Runners
An API that answers quickly on a laptop can answer slowly on a busy CI runner, and every wait that guesses fails there first. Stub the response when the spec tests UI logic, and give a genuinely slow command its own timeout instead of raising the global one.
How Do Cypress Test Retries Work?
Cypress does not retry failed tests by default. Set retries with runMode and openMode, and Cypress reruns a failed test up to that many extra times before it marks the test as failed.
Add the option to the Cypress config file. With runMode: 2, a test gets up to three attempts under cypress run, and openMode: 0 keeps cypress open strict while you write tests.
const { defineConfig } = require('cypress');
module.exports = defineConfig({
retries: {
runMode: 2, // cypress run, used in CI
openMode: 0, // cypress open, used while you write tests
},
});
| Setting | Default | What it does |
|---|---|---|
| retries.runMode | 0 | Extra attempts for a failed test under cypress run. |
| retries.openMode | 0 | Extra attempts for a failed test under cypress open. |
| retries: 2 | None | One number that applies to both modes. |
| { retries: ... } on a test or suite | Inherits config | Overrides the config for one it() or describe(). |
it('allows user to login', { retries: { runMode: 2, openMode: 1 } }, () => {
// ...
});
A retry reruns the test from the start, including its beforeEach and afterEach hooks. Failures in before and after hooks do not trigger a retry. Each failed attempt keeps its own screenshot, suffixed with the attempt number, and Cypress.currentRetry returns the current attempt inside a test. Cypress also offers experimental retries, which let you set the conditions under which a test counts as passed or failed; the test retries guide describes them.
Use retries to measure flake, not to hide it:
- runMode only - keep openMode at 0 so a flaky test fails while you are still writing it.
- Low numbers - one or two extra attempts absorb a timing hiccup. A higher number mostly makes a broken test slower to fail.
- Per-test overrides - add one only with a comment that names the reason and the owner, so it does not outlive the fix.
Retry-ability vs Test Retries
Retry-ability and test retries solve different problems. Retry-ability keeps one command from failing because the app is a moment behind. A test retry reruns everything after a failure has already happened.
| Retry-ability | Test retries | |
|---|---|---|
| What is retried | Queries such as cy.get() and .find(), with the assertions linked to them | The whole test, including its beforeEach and afterEach hooks |
| When it runs | Inside one test, on every query and assertion | After an attempt fails |
| Default limit | 4 seconds (defaultCommandTimeout) | 0 retries |
| Effect on flake | Prevents timing failures | Hides a flake unless you track it |
Per the Cypress retry-ability guide, non-query commands such as actions execute once. That is why the assertion belongs on the query chain. The Cypress should command guide lists the assertions you can chain.
// cy.get() and .should() are linked: Cypress re-queries until the assertion passes (4 seconds by default)
cy.get('#orders .order').should('have.length', 3);
// .then() runs once with whatever the DOM holds at that moment
cy.get('#orders').then(($ul) => {
expect($ul.find('.order').length).to.eq(3);
});
Running Cypress Suites on TestMu AI
TestMu AI runs the Cypress suite you already have and does not replace Cypress. Automation Cloud is cloud test automation infrastructure with AI-native debugging, which gives a flaky spec a controlled place to run and leave evidence behind:
- Your existing specs - Cypress, Selenium, Playwright and Puppeteer scripts run as they are, with no rewrite and no proprietary test format.
- Browser and OS coverage - 3,000+ browser and OS combinations are hosted for you, so a flake that appears on one configuration can be reproduced there.
- Parallel execution - sessions run concurrently across configurations instead of one after another.
- Artifacts on every run - network logs, console logs, video, screenshots and command logs are captured automatically, so a failed attempt keeps what the page and the network did.
- AI-native triage and root cause analysis - network, console and framework logs are correlated to localize the likely cause of a failure. Treat it as a lead to verify, not a verdict.
- LT Tunnel - specs can run against a local, staging or privately hosted app through an encrypted tunnel, with no public URL.
Moving a suite to the cloud does not fix a timing bug in the test. The fixes below still apply, and the cloud supplies the parallel capacity and the evidence to show they hold.
Note: Run your Cypress suites with logs, video and screenshots on every run. Try TestMu AI Today
Reproducing a Flaky Test
A flake you cannot reproduce is a flake you will retry forever. These steps make a timing flake fail on demand:
- Set retries to 0 for the spec, so a flaky attempt shows up as a failure instead of a pass on retry.
- Repeat the test inside one spec. Cypress._.times() generates as many copies as you need.
- Slow the response. A one second delay on the request exposes any test that guesses at timing.
- Run in the browser and with the same flags as CI, then open the failed attempt in Cypress debugging tools to see what the DOM held.
// Twenty copies of the same test in one spec, with retries left at 0
Cypress._.times(20, (i) => {
it('orders load, run ' + (i + 1) + ' of 20', () => {
cy.intercept('GET', '/api/orders').as('orders');
cy.visit('/');
cy.get('#load').click();
cy.wait('@orders');
cy.get('.order').should('have.length', 3);
});
});
// Slow the response by one second to expose a test that guesses at timing
cy.intercept('GET', '/api/orders', (req) => {
req.on('response', (res) => res.setDelay(1000));
});
Timings differ for every app, so run the repeated spec against your own app before and after each fix. A spec that passes all twenty copies with retries at 0 is a stronger signal than one green run.
Detecting Flaky Specs in CI
Detect flaky Cypress specs in CI by tracking every test that fails and then passes on retry. Cypress Cloud flags those tests, and HyperExecute adds muting and automatic retries on top.
Cypress Cloud
With runs recorded to Cypress Cloud, the Test Results tab has a Flaky filter that shows tests that retried and then passed. The same tests carry a Flaky badge on the Latest Runs and Run Details pages. Test Case History shows the failed attempts with their screenshots and errors, plus the flake rate for the test. The Flake Detection and Flake Alerting features are described in the Cypress Cloud flaky test management guide.
HyperExecute
TestMu AI runs Cypress on HyperExecute, its AI-native test orchestration cloud, so the same specs run in parallel with intelligent test splitting and no changes to the specs. For flaky specs it adds two controls:
- Test muting - mute a test from the three-dot menu on its result, or turn on automatic muting (the default threshold is 5 consecutive failures). A muted test stays inactive until someone unmutes it, so an unreliable test stops blocking the build while its owner fixes it.
- Automatic retries - set retryOnFailure and maxRetries (1 to 5, default 2) in the YAML file and HyperExecute reruns a test command that fails with a non-zero exit code, which absorbs transient failures without touching your specs.
The setup steps are in the Cypress integration with HyperExecute guide, and the muting settings are in the HyperExecute test muting guide.
Test Insights
One run cannot show which specs flake. The consistently failing tests view in Test Insights ranks tests by how often they fail across the run history, so the chronic offenders become a fix list. It surfaces candidates for a person to fix, and it needs enough runs to count.
A Test That Passes on Retry
A green build after a retry still hides a flaky test. Treat every retried pass as a defect with three steps:
- Log it. Keep the list of tests that needed a retry, from Cypress Cloud or your CI report.
- Give it an owner, so one person is responsible for the fix.
- Fix the cause with a deadline. A muted or quarantined test that nobody fixes is a test you no longer have.
Fixes With Before/After Code
Each fix below replaces a guess with a condition. Apply them one at a time and rerun the repeated spec from the previous section after each change.
1. Replace a Fixed Wait With a Request Alias
Before, the test waits half a second and checks once. After, it waits for the request itself and then asserts on a query that retries.
// Before
cy.get('#load').click();
cy.wait(500); // a guess about how long the API takes
cy.get('#orders').then(($ul) => {
expect($ul.find('.order').length).to.eq(3);
});
// After
cy.intercept('GET', '/api/orders').as('orders');
cy.get('#load').click();
cy.wait('@orders'); // continues when the request finishes
cy.get('.order').should('have.length', 3);
2. Register the Intercept Before the Page Loads
Register cy.intercept() before cy.visit() or before the click that sends the request. Otherwise the alias never matches.
// Before
cy.visit('/onload');
cy.intercept('GET', '/api/orders').as('orders'); // the request has already been sent
cy.wait('@orders');
// After
cy.intercept('GET', '/api/orders').as('orders'); // registered first
cy.visit('/onload');
cy.wait('@orders');
cy.get('.order').should('have.length', 3);
3. Move the Check From .then() to .should()
Use .should() for assertions on the page. Keep .then() for code that must run once, such as reading a value into a variable.
// Before
cy.get('#load').click();
cy.get('#orders').then(($ul) => {
expect($ul.find('.order').length).to.eq(3); // runs once, cannot wait
});
// After
cy.get('#load').click();
cy.get('#orders .order').should('have.length', 3); // retries until it passes
4. Reset State Before Each Test
Create the data a test needs inside that test's own setup, so it passes alone and in any order. The seeding calls below stand in for your app's own API or a cy.task() that talks to your database.
// Before: the second test needs the order the first test created
it('creates an order', () => {
cy.visit('/');
cy.get('#add-order').click();
});
it('lists the order', () => {
cy.visit('/orders');
cy.get('.order').should('have.length', 1); // fails when run alone or out of order
});
// After: every test seeds its own data
beforeEach(() => {
cy.request('POST', '/api/test/reset');
cy.request('POST', '/api/orders', { item: 'book' });
});
it('lists the order', () => {
cy.visit('/orders');
cy.get('.order').should('have.length', 1);
});
5. Stub the Backend and Time the Slow Command
When a spec tests how the UI renders data, serve that data from a fixture so a slow or changing backend cannot fail it. When a command is genuinely slow, pass { timeout } to that command and leave the global timeout alone.
// Before: depends on the real API and its response time
cy.visit('/');
cy.get('.order', { timeout: 1000 }).should('have.length', 3);
// After: a fixture answers the request, and the slow report gets its own timeout
cy.intercept('GET', '/api/orders', { fixture: 'orders.json' }).as('orders');
cy.visit('/');
cy.wait('@orders');
cy.get('.order').should('have.length', 3);
cy.get('#monthly-report', { timeout: 15000 }).should('be.visible'); // this command is slow by design
To start, set retries: { runMode: 2 } in CI, list every spec that passes only on retry, and apply these patterns to the first one. The Cypress best practices guide covers selectors and the rest of a stable suite, and running Cypress with GitHub Actions covers the CI side. To validate the fundamentals, the free Cypress 101 certification from TestMu AI includes a hands-on coding assignment.
Author
Sri Harsha is Engineering Manager of the Open Source Program Office at TestMu AI, where he leads open-source engineering behind the Selenium and Appium automation grid and builds agentic AI systems for quality engineering. He is a member of the Selenium Technical Leadership Committee and a committer to WebdriverIO and Appium, and was recognized with the LambdaTest Delta Award 2023 for Best Contributor in open-source testing. He brings over 10 years of experience in software testing and automation, with earlier roles at EPAM Systems and ZenQ. Sri Harsha holds a B.Tech in Computer Science from Jawaharlal Nehru Technological University.
Reviewer
Himanshu Sheth is the Director of Marketing (Technical Content) at TestMu AI, with over 8 years of hands-on experience in Selenium, Cypress, and other test automation frameworks. He has authored more than 130 technical blogs for TestMu AI, covering software testing, automation strategy, and CI/CD. At TestMu AI, he leads the technical content efforts across blogs, YouTube, and social media, while closely collaborating with contributors to enhance content quality and product feedback loops. He has done his graduation with a B.E. in Computer Engineering from Mumbai University. Before TestMu AI, Himanshu led engineering teams in embedded software domains at companies like Samsung Research, Motorola, and NXP Semiconductors. He is a core member of DZone and has been a speaker at several unconferences focused on technical writing and software quality.
Cypress Flaky Tests FAQs
Did you find this page helpful?
More Related Blogs
TestMu AI forEnterprise
Get access to solutions built on Enterprise
grade security, privacy, & compliance
- Advanced access controls
- Advanced data retention rules
- Advanced Local Testing
- Premium Support options
- Early access to beta features
- Private Slack Channel
- Unlimited Manual Accessibility DevTools Tests




