teamcapybara/capybara · error · Capybara::ElementNotFound
Unable to find #{query.applied_description}
Error message
Unable to find #{query.applied_description} What it means
The canonical Capybara::ElementNotFound from Finders#find (synced_resolve): after retrying for the query's wait time, the selector plus applied filters (:text, :visible, etc.) resolved to zero elements. The message embeds query.applied_description, e.g. "Unable to find visible css 'a' with text 'Continue'". find is an assertion-grade API: it blocks and retries, then fails hard instead of returning nil.
Source
Thrown at lib/capybara/node/finders.rb:312
options = { minimum: 1 }.merge(options) unless options_include_minimum?(options)
all(*args, **options, &optional_filter_block).first
end
private
def synced_resolve(query)
synchronize(query.wait) do
if prefer_exact?(query)
result = query.resolve_for(self, true)
result = query.resolve_for(self, false) if result.empty? && query.supports_exact? && !query.exact?
else
result = query.resolve_for(self)
end
if ambiguous?(query, result)
raise Capybara::Ambiguous, "Ambiguous match, found #{result.size} elements matching #{query.applied_description}"
end
raise Capybara::ElementNotFound, "Unable to find #{query.applied_description}" if result.empty?
result.first
end.tap(&:allow_reload!)
end
def ambiguous?(query, result)
%i[one smart].include?(query.match) && (result.size > 1)
end
def prefer_exact?(query)
%i[smart prefer_exact].include?(query.match)
end
def options_include_minimum?(opts)
%i[count minimum between].any? { |key| opts.key?(key) }
end
def parentView on GitHub (pinned to 15b5fdb76e)
Solutions
- Raise the wait: find('#notice', wait: 10) or Capybara.default_max_wait_time = 5
- Copy the exact locator from the failure message and check it against current DOM (save_and_open_page or browser devtools)
- Drop or adjust mismatched filters: visible: false for hidden nodes, exact_text: false for partial text
- Ensure the element is genuinely exposed first (click the tab, scroll, switch frame via within_frame)
Example fix
# before
find('#status-message').text
# after
find('#status-message', wait: 10).text
# or assert with waiting semantics first
expect(page).to have_css('#status-message', wait: 10)
find('#status-message').text Defensive patterns
Strategy: retry
Validate before calling
if page.has_selector?('#notice', wait: 10)
find('#notice').text
else
save_and_open_page # debug the actual DOM
end Try / catch
begin
find('#notice', wait: 10)
rescue Capybara::ElementNotFound => e
raise unless page.has_selector?('.fallback-notice', wait: 2)
find('.fallback-notice')
end Prevention
- Use waiting find/RSpec matchers instead of driver-native queries
- Give find an explicit wait: for elements rendered by slow AJAX
- Assert a stable container exists before finding children inside it
When it happens
Trigger: find('#notice') before an AJAX action has rendered it; find('a', text: 'Sign out') when the text differs by whitespace/case; find('.item', visible: true) when the element exists but is hidden; a typo'd locator find('#sucess'); find on a page that hasn't finished navigating.
Common situations: Race between test step and async rendering when default_max_wait_time is short (common on slow CI), elements behind tabs/accordions that are display:none until opened, React/Vue re-rendering replacing a node mid-find, iframe scope mistakes (find inside page when the element is in a frame).
Related errors
- Ambiguous match, found #{result.size} elements matching #{qu
- result.failure_message
- result.failure_message
- res.join(' or ')
- result.negative_failure_message
AI-assisted analysis of teamcapybara/capybara@15b5fdb76e (2026-08-21).
Data as JSON: /api/errors/2746badcd24b3bc4.
Report an issue: GitHub.