Skip to content
Latchkey

Cypress "Timed out retrying after 4000ms" - Fix Flaky Assertions

Cypress retried a command/assertion for its timeout window (4000 ms by default) and the condition never became true. Usually the element renders slower than the timeout in CI, or the selector no longer matches.

What this error means

A command fails with "Timed out retrying after 4000ms: Expected to find element: 'X', but never found it." It often passes locally and on the headed app but flakes on a slower CI runner.

Cypress output
Timed out retrying after 4000ms: Expected to find element:
`[data-testid="submit"]`, but never found it.

Common causes

Element renders slower than the timeout in CI

On a loaded runner the app paints later than locally. The default 4s retry window expires before the element appears - a timing-only flake.

Selector no longer matches

A markup or data-testid change means the selector matches nothing. This is deterministic, not flaky, and a longer timeout will not help.

Asserting before the app settles

Asserting immediately after an action that triggers async work (XHR, animation) races the UI; without retry-able assertions it can miss the final state.

How to fix it

Assert on the actual readiness signal

Wait on the network or a visible state change rather than a fixed sleep, so the test is fast when possible and patient when needed.

cart.cy.js
cy.intercept('GET', '/api/cart').as('cart');
cy.visit('/cart');
cy.wait('@cart');
cy.get('[data-testid="submit"]').should('be.visible');

Raise the timeout for genuinely slow steps

spec.cy.js
cy.get('[data-testid="submit"]', { timeout: 15000 }).should('exist');
// or globally in cypress.config.js: defaultCommandTimeout

How to prevent it

  • Wait on intercepted requests, never cy.wait(<ms>).
  • Use stable data-testid selectors decoupled from styling.
  • Tune defaultCommandTimeout for the slowest CI tier.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →