teamcapybara/capybara · error · Capybara::ExpectationNotMet

result.failure_message

Error message

result.failure_message

What it means

Node::Finders#all raises Capybara::ExpectationNotMet (message built by Result#failure_message: 'expected to find <description> ... but there were no matches' or 'found N matches: ...') when the number of resolved elements does not satisfy the count options after waiting. `all` injects minimum: 1 unless you pass :count, :minimum or :between, so even finding zero plain elements can raise. It retries inside synchronize(query.wait) until the deadline before giving up.

Source

Thrown at lib/capybara/node/finders.rb:267

      #   When `true` allows elements to be reloaded if they become stale. This is an advanced behavior and should only be used
      #   if you fully understand the potential ramifications. The results can be confusing on dynamic pages. Defaults to `false`
      # @overload all([kind = Capybara.default_selector], locator = nil, **options)
      # @overload all([kind = Capybara.default_selector], locator = nil, **options, &filter_block)
      #   @yieldparam element [Capybara::Node::Element]  The element being considered for inclusion in the results
      #   @yieldreturn [Boolean]                     Should the element be considered in the results?
      # @return [Capybara::Result]                   A collection of found elements
      # @raise [Capybara::ExpectationNotMet]         The number of elements found doesn't match the specified conditions
      def all(*args, allow_reload: false, **options, &optional_filter_block)
        minimum_specified = options_include_minimum?(options)
        options = { minimum: 1 }.merge(options) unless minimum_specified
        options[:session_options] = session_options
        query = Capybara::Queries::SelectorQuery.new(*args, **options, &optional_filter_block)
        result = nil
        begin
          synchronize(query.wait) do
            result = query.resolve_for(self)
            result.allow_reload! if allow_reload
            raise Capybara::ExpectationNotMet, result.failure_message unless result.matches_count?

            result
          end
        rescue Capybara::ExpectationNotMet
          raise if minimum_specified || (result.compare_count == 1)

          Result.new([], nil)
        end
      end
      alias_method :find_all, :all

      ##
      #
      # Find the first element on the page matching the given selector
      # and options. By default {#first} will wait up to {Capybara.configure default_max_wait_time}
      # seconds for matching elements to appear and then raise an error if no matching
      # element is found, or `nil` if the provided count options allow for empty results.
      #

View on GitHub (pinned to 15b5fdb76e)

Solutions

  1. Verify the actual count first (page.all('li').size) and align the count option with reality
  2. Raise the wait: page.all('li', count: 3, wait: 5) or Capybara.default_max_wait_time = 5
  3. Relax the constraint to minimum:/between: when exact count is brittle (e.g. between: 2..4)
  4. If items are filtered out by :text/:visible, remove or correct the filter so intended items count

Example fix

# before
page.all('li.item', count: 3)

# after
page.all('li.item', count: 3, wait: 5)
# or tolerate async loading
page.all('li.item', minimum: 3, wait: 5)
Defensive patterns

Strategy: retry

Validate before calling

expected = 3
found = page.all('li.item', wait: 5).size
page.all('li.item', count: expected) if found == expected

Try / catch

begin
  page.all('li.item', count: 3, wait: 5)
rescue Capybara::ExpectationNotMet => e
  puts "actual: #{page.all('li.item').size} — #{e.message}"
  raise
end

Prevention

When it happens

Trigger: page.all('li', count: 3) when the list has 2 or 4 items; page.all('.item', minimum: 5) on a partially rendered list; page.all('td') inside a table that has not rendered yet within default_max_wait_time. Note the rescue in the source: when only the implicit minimum: 1 applies and too many elements were found (Result#compare_count == 1), `all` silently returns an empty Result instead of raising.

Common situations: Async/paged content still loading when the count is taken; wrong expectation of item count after filtering; slow CI making the built-in wait too short; mixed-content lists where some items match filters (:text, :visible) and others do not — the failure message lists 'Also found ... which matched the selector but not all filters'.

Related errors


AI-assisted analysis of teamcapybara/capybara@15b5fdb76e (2026-08-21). Data as JSON: /api/errors/d73d54cc80a6b440. Report an issue: GitHub.