cypress cy.get() timeout in CI only
Shows how to fix Cypress cy.get() timeouts that only happen in CI. Covers raising defaultCommandTimeout, switching to stable data attributes, and waiting on network requests instead of fixed sleeps. Use when a suite is green locally but selectors time out on CI runners. Not for timeouts that also fail locally, for non-Cypress E2E tools, or for general CI slowness with no Cypress failure.
TL;DR
Raise defaultCommandTimeout in your CI config and wait on network requests instead of the clock. CI runners are slower and shared, so elements that appear in a second locally can take several seconds there. A timeout that only happens in CI is almost always an optimistic wait, not a broken test.
Error
CypressError: Timed out retrying after 4000ms: Expected to find element: `.submit-btn`, but never found it.Steps
- Run the failing spec locally three times in a row. Expected: green all three times, which confirms the failure is CI-only.
- Replace fragile selectors with stable data attributes, for example cy.get('[data-cy=submit]'). Expected: the selector matches exactly one element and does not depend on CSS classes or copy.
- In cypress.config.js, raise the retry timeout:
module.exports = {
e2e: {
defaultCommandTimeout: 10000,
},
}Expected: the config loads and the new value appears in the Cypress settings panel.
- If the element renders after an API call, wait on the request instead of guessing:
cy.intercept('GET', '/api/orders').as('orders')
cy.visit('/orders')
cy.wait('@orders')
cy.get('[data-cy=order-row]').should('have.length', 3)Expected: the test proceeds when the response arrives, with no fixed sleep.
- Delete any cy.wait(2000) style fixed sleeps in the spec. Expected: no hard-coded sleeps remain.
- Re-run the spec in CI. Expected: green.
When to use
- cy.get() or cy.contains() times out in CI but passes on your machine
- Failures cluster on specs that render after API responses
- The suite recently gained selectors tied to animations or lazy loading
When not to use
- The timeout also fails locally (fix the selector or the app instead)
- Every spec fails in CI (check baseUrl, services, and the Cypress binary install)
- The failure is a real assertion mismatch rather than a missing element
Tool compatibility
- Cypress 12 through 14, end-to-end testing
- Any CI provider that runs cypress run, including GitHub Actions, CircleCI, and Jenkins
- Chrome, Edge, Firefox, and Electron runners
Variant phrasings
Timed out retrying after 4000ms in GitHub Actions only
Same fix. Shared Actions runners are slow, so the 4 second default is the first thing to raise.
Element never found in the Jenkins pipeline but fine locally
Check whether Jenkins serves a different baseUrl or an older build. If the page itself differs, no timeout will help.
Why it happens
Cypress retries cy.get() until defaultCommandTimeout expires. Local machines are fast and warm. CI runners are cold, shared, and often throttled, so rendering and API responses land later. A selector that needs five seconds in CI fails at the four second default while passing locally in one.
Edge cases
- Still red after raising the timeout to 15000ms: the element genuinely never renders in CI. Check baseUrl, feature flags, and seeded test data.
- Flaky in only one CI browser: browser-specific rendering or animation timing. Scope the timeout change to that browser config.
- Passes alone in CI but fails in the full suite: test pollution. Check testIsolation and state shared between specs.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_xJ80SjbgL9m2A9iwPAaSfw
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.