teamcapybara/capybara · error · Capybara::Ambiguous
Ambiguous match, found #{result.size} elements matching #{qu
Error message
Ambiguous match, found #{result.size} elements matching #{query.applied_description} What it means
Finders#find (via synced_resolve) raises Capybara::Ambiguous when more than one element matches and the query's match mode is :one or :smart (smart is the default). With :smart, find first tries an exact locator match and only falls back to partial matching, so the error means even the exact pass returned multiple elements. It is the counterpart of ElementNotFound: one locator must resolve to exactly one element.
Source
Thrown at lib/capybara/node/finders.rb:310
#
def first(*args, **options, &optional_filter_block)
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) }
endView on GitHub (pinned to 15b5fdb76e)
Solutions
- Scope the search to a unique container: find('#user-1').find('a', text: 'Delete')
- Make the locator exact and unique: find('a.exact-class', text: 'Delete', exact_text: true)
- Accept the first match explicitly: find('a', text: 'Delete', match: :first) or all('a', text: 'Delete').first
- Pick by index/attributes only when order is stable: all('a', text: 'Delete')[1]
Example fix
# before
find('a', text: 'Delete').click # two Delete links exist
# after
find('#row-42').find('a', text: 'Delete').click
# or
all('a', text: 'Delete').first.click Defensive patterns
Strategy: validation
Validate before calling
matches = page.all('a', text: 'Delete')
raise 'ambiguous: scope the selector' if matches.size > 1
matches.first&.click Type guard
def unique_match?(node, *args, **opts, &block) node.all(*args, **opts, &block).size == 1 end
Try / catch
begin
find('a', text: 'Delete')
rescue Capybara::Ambiguous => e
all('a', text: 'Delete').first # deliberate first-match policy
end Prevention
- Scope find calls inside unique containers (within('#row-42') { ... })
- Make locators specific enough to be unique: ids, data attributes, exact classes
- Only opt into match: :first when element order is guaranteed by the app
When it happens
Trigger: find('a', text: 'Delete') on a page with two 'Delete' links (smart mode tries exact text first, still finds 2); find('li') on a multi-item list; find('button') with multiple buttons under match: :one; find(field: 'Name') when both a label and an aria-label produce separate matches.
Common situations: Repeated UI components (rows each containing an Edit button), icons with identical accessible names, test data duplicated in the DOM, switching default_match to :one globally and having previously-tolerated multiple matches now fail.
Related errors
- Unable to find #{query.applied_description}
- result.failure_message
- res.join(' or ')
- result.negative_failure_message
- Item does not match the provided selector
AI-assisted analysis of teamcapybara/capybara@15b5fdb76e (2026-08-21).
Data as JSON: /api/errors/c522318282a9438c.
Report an issue: GitHub.