teamcapybara/capybara · warning

Locator #{locator.class}:#{locator.inspect} for selector #{n

Error message

Locator #{locator.class}:#{locator.inspect} for selector #{name.inspect} must #{locator_description}. This will raise an error in a future version of Capybara. Called from: #{Capybara::Helpers.filter_backtrace(caller)}

What it means

Every Capybara selector declares the locator types it accepts (its locator_types, e.g. String for :css, :xpath, :field). After generating the expression, Selector#call verifies locator_valid?; when the locator is non-nil and does not match the declared types (or does not respond to a required method), Capybara warns with this message, including the caller's backtrace, and states it will raise in a future version. nil locators are allowed.

Source

Thrown at lib/capybara/selector/selector.rb:69

    def enable_aria_role
      @config[:enable_aria_role]
    end

    def test_id
      @config[:test_id]
    end

    def call(locator, **options)
      if format
        raise ArgumentError, "Selector #{@name} does not support #{format}" unless expressions.key?(format)

        instance_exec(locator, **options, &expressions[format])
      else
        warn 'Selector has no format'
      end
    ensure
      unless locator_valid?(locator)
        Capybara::Helpers.warn(
          "Locator #{locator.class}:#{locator.inspect} for selector #{name.inspect} must #{locator_description}. " \
          'This will raise an error in a future version of Capybara. ' \
          "Called from: #{Capybara::Helpers.filter_backtrace(caller)}"
        )
      end
    end

    def add_error(error_msg)
      errors << error_msg
    end

    def expression_for(name, locator, config: @config, format: current_format, **options)
      Selector.new(name, config: config, format: format).call(locator, **options)
    end

    # @api private
    def with_filter_errors(errors)
      old_errors = @errors

View on GitHub (pinned to 15b5fdb76e)

Solutions

  1. Convert the locator to the selector's expected type, usually String: fill_in(user.id.to_s, with: 'x') or find(:css, selector.to_s).
  2. Pass the human-readable attribute the selector matches (label text, name, placeholder) instead of a numeric identifier.
  3. For custom selectors, declare locator types matching what callers send (e.g. expression parameter :String vs accepting objects that respond_to :to_str).

Example fix

# before
fill_in(user.id, with: 'comment') # locator is Integer -> warning

# after
fill_in(user.id.to_s, with: 'comment')
Defensive patterns

Strategy: type-guard

Validate before calling

# Coerce numeric/odd-typed locators before calling selectors
locator = locator.to_s unless locator.is_a?(String) || locator.nil?
page.find(:field, locator, **options)

Type guard

def valid_locator?(locator)
  locator.nil? || locator.is_a?(String) || locator.respond_to?(:to_str)
end

Prevention

When it happens

Trigger: page.find(:css, 5) or find(:xpath, some_integer); fill_in(user.id, with: 'x') where the numeric ID is passed instead of the field's label/name string; passing a model object, Symbol, or Hash to a selector whose expression expects a String.

Common situations: Numeric IDs from factories/sequences flowing into locators (user.id instead of user.name or user.id.to_s); passing ActiveRecord objects instead of their string attributes; custom selectors whose declared expression parameter type differs from what callers actually send; forward-porting code to a Capybara version that added this validation.

Related errors


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