Skip to content
Latchkey

Capybara::ElementNotFound in a system test in CI

Capybara could not locate an element within its wait time. In headless CI this is usually a timing or rendering difference: the element appears later, behind JS that did not run, or under an asset that did not build.

What this error means

A system/feature spec fails with Capybara::ElementNotFound naming the selector it could not find. It often passes locally with a visible browser but fails headless in CI.

rspec
Capybara::ElementNotFound:
       Unable to find visible button "Save" within #<Capybara::Node...>

Common causes

Element appears after the wait

JavaScript renders the element after Capybara stopped waiting, or an animation/transition delays it past the default wait time.

Assets/JS not built for tests

The JS bundle or stylesheet that would render the element was not compiled in the test environment, so the control never appears.

Wrong or non-visible selector

The element exists but is hidden, or the locator differs from what is actually rendered in CI.

How to fix it

Wait for the right state and build assets

  1. Use Capybara matchers that wait (have_button, have_selector) instead of immediate finds, and increase Capybara.default_max_wait_time if needed.
  2. Precompile JS/CSS for the test environment so the UI renders.
  3. Assert on the actual visible selector; debug with a saved screenshot/page on failure.

Capture failure artifacts

Ruby
# config: save a screenshot on system-test failure
page.save_screenshot('tmp/capybara/failure.png')
puts page.html   # inspect what actually rendered

How to prevent it

  • Use Capybara waiting matchers, not bare finds, for async UI.
  • Build JS/CSS assets before system tests.
  • Save screenshots/HTML on failure to diagnose headless rendering.

Related guides

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