Diagnosing Persistent Flakiness in Cypress Tests Despite Retry Configurations
Learn how to diagnose and fix Cypress tests that fail consistently despite having retries enabled, focusing on async race conditions, DOM instability, and network mocking.
08 Aug 2026, 21:54 UTC

The Problem: Retries Aren't Fixing Your Flaky Tests
You have enabled retries in your Cypress configuration to handle intermittent failures, but specific tests continue to fail across every attempt. When a test fails consistently despite retries, the issue is rarely a "random" glitch; it is typically a deterministic race condition or a configuration gap that makes the test unstable regardless of how many times it is rerun.
Quick Diagnostic Matrix
| Symptom | Likely Cause | Diagnostic Tool |
|---|---|---|
| Test fails once and stops immediately | Retry config not applied | cypress.config.js |
| Failure happens at the same step every retry | Unawaited async operation | Cypress Command Log |
| Failure varies between 404, 500, or Timeout | Unstable network/API | Browser DevTools Network Tab |
| "Element not found" or "Detached from DOM" | DOM instability/Animations | Cypress Studio / Selector Debugging |
Step 1: Verify Retry Activation
Before debugging the test logic, ensure Cypress is actually attempting the retries. In Cypress 12.x, configuration is handled in cypress.config.js. If the retries key is missing or placed outside the e2e block, the runner will execute the test only once.
// cypress.config.js
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
retries: {
runMode: 2, // Retries in 'cypress run' (CI)
openMode: 0 // Retries in 'cypress open' (Local)
},
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
});
Verification: Run the test via CLI using npx cypress run --spec path/to/test.cy.js. Check the terminal output; it should explicitly state (Attempt 1), (Attempt 2), etc., if the test fails.
Step 2: Identify Async Race Conditions
A common cause of persistent flakiness is the "unawaited promise." While Cypress commands are queued, mixing them with native JavaScript async/await or setTimeout can cause the test to move to the next assertion before the application state has updated.
The Fix: Replace native async logic with cy.intercept() and cy.wait(). This ensures Cypress synchronizes with the network request rather than relying on a hard-coded timer.
// Avoid: await someAsyncFunction();
// Better: Intercept the network call triggered by the function
cy.intercept('POST', '/api/save-user').as('saveUser');
cy.get('[data-cy="submit"]').click();
cy.wait('@saveUser').its('response.statusCode').should('eq', 200);
cy.get('.success-message').should('be.visible');
Step 3: Resolve DOM Instability
If you see errors like element is detached from the DOM, the application is likely re-rendering the component between the time Cypress finds the element and the time it attempts to click it. This often happens during animations or framework-level state updates (e.g., React/Vue re-renders).
- Check for Animations: If an element fades in, a standard
.should('be.visible')might pass while the element is still moving, causing a click to miss. - Use Guard Assertions: Ensure the element is in a stable state before interacting.
Example Guard: Instead of cy.get('.btn').click(), use cy.get('.btn').should('not.be.disabled').click() to ensure the UI has finished its loading state.
Step 4: Isolate Network Dependencies
If the test fails intermittently due to 500 errors or slow API responses, the flakiness is external to your frontend code. Relying on a live staging environment for every test run introduces non-deterministic failure points.
The Fix: Stub the unstable endpoint. This removes the network variable entirely.
// Run this in the test file or beforeEach block
cy.intercept('GET', '/api/unstable-endpoint', {
statusCode: 200,
body: { data: 'mocked-stable-response' }
}).as('getStableData');
Risk: Over-mocking can lead to "green" tests that pass in CI but fail in production because the actual API contract changed. Periodically run a small set of "smoke tests" against the real API to verify the contract.
Escalation Criteria
If you have verified the retry config, implemented network intercepts, and added DOM guards, but the test still fails unpredictably, escalate the issue to the infrastructure team if:
- The failure only occurs in the CI environment (suggesting resource contention or CPU throttling).
- The failure coincides with
OutOfMemoryerrors in the browser process. - The test fails only when run in parallel across multiple machines.
Rollback and Cleanup
If you modified cypress.config.js to increase retries and find that CI runtimes have increased significantly without a corresponding increase in stability, revert the retries value to 0 or 1. High retry counts often mask genuine regressions rather than solving flakiness.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.