teamcapybara/capybara · error · Capybara::ExpectationNotMet

res.join(' or ')

Error message

res.join(' or ')

What it means

assert_any_of_selectors raises ExpectationNotMet only when every supplied locator failed: it maps each locator through assert_selector, collecting each Capybara::ExpectationNotMet message, and short-circuits (break nil) on the first success. The final message is the per-locator failure messages joined with ' or ', so you see exactly why each alternative did not match.

Source

Thrown at lib/capybara/node/matchers.rb:211

      # It accepts all options that {Capybara::Node::Finders#all} accepts,
      # such as `:text` and `:visible`.
      #
      # The `:wait` option applies to all of the selectors as a group, so any of the locators must be present
      # within `:wait` (defaults to {Capybara.configure default_max_wait_time}) seconds.
      #
      # @overload assert_any_of_selectors([kind = Capybara.default_selector], *locators, **options)
      #
      def assert_any_of_selectors(*args, wait: nil, **options, &optional_filter_block)
        wait = session_options.default_max_wait_time if wait.nil?
        selector = extract_selector(args)
        synchronize(wait) do
          res = args.map do |locator|
            assert_selector(selector, locator, options, &optional_filter_block)
            break nil
          rescue Capybara::ExpectationNotMet => e
            e.message
          end
          raise Capybara::ExpectationNotMet, res.join(' or ') if res

          true
        end
      end

      ##
      #
      # Asserts that a given selector is not on the page or a descendant of the current node.
      # Usage is identical to {#assert_selector}.
      #
      # Query options such as `:count`, `:minimum`, `:maximum`, and `:between` are
      # considered to be an integral part of the selector. This will return
      # `true`, for example, if a page contains 4 anchors but the query expects 5:
      #
      #     page.assert_no_selector('a', minimum: 1) # Found, raises Capybara::ExpectationNotMet
      #     page.assert_no_selector('a', count: 4)   # Found, raises Capybara::ExpectationNotMet
      #     page.assert_no_selector('a', count: 5)   # Not Found, returns true
      #

View on GitHub (pinned to 15b5fdb76e)

Solutions

  1. Read the joined message: each segment is a full assert_selector failure explaining that alternative's mismatch
  2. Fix the wrong locators or add the actually-rendered alternative ('Apply') to the list
  3. Verify you are on the page that can render any alternative (assert current_path or a page-level marker first)
  4. Extend wait if the alternatives appear asynchronously: assert_any_of_selectors(:css, '#a', '#b', wait: 5)

Example fix

# before
page.assert_any_of_selectors(:field, 'Email', 'E-mail address')

# after
page.assert_any_of_selectors(:field, 'Email', 'E-mail address', 'Email address', wait: 5)
Defensive patterns

Strategy: retry

Validate before calling

present = %w[#error #warning #notice].any? { |sel| page.has_selector?(sel, wait: 5) }
page.assert_any_of_selectors(:css, '#error', '#warning', '#notice') if present

Try / catch

begin
  page.assert_any_of_selectors(:css, '#a', '#b', wait: 5)
rescue Capybara::ExpectationNotMet => e
  save_and_open_page # none of the alternatives matched — inspect DOM
  raise
end

Prevention

When it happens

Trigger: page.assert_any_of_selectors(:css, '#error', '#warning', '#notice') when none of the three exist after the wait; asserting either of two button texts ('Save', 'Submit') when the app renders 'Apply'; passing a selector kind explicitly then locators that all fail filters like visible: true.

Common situations: Apps with versioned/branched UI where either of two labels may render, A/B-tested copy in tests, refactor where both old and new markup were expected but the page shows neither (navigation failed, wrong page loaded), typo in one of the alternatives making the whole chain fail.

Related errors


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