SeleniumHQ/selenium · error · ArgumentError

expected #{arg.inspect}:#{arg.class} to respond to #shift

Error message

expected #{arg.inspect}:#{arg.class} to respond to #shift

What it means

Raised by SearchContext#extract_args (private) when find_element or find_elements is called with exactly one argument that does not respond to the :shift method (i.e., not a Hash or Array-like object). The method expects either two positional args (how, what) or a single Hash/Array that can be shifted into a key-value or [key, value] pair.

Source

Thrown at rb/lib/selenium/webdriver/common/search_context.rb:99

        how, what = extract_args(args)

        by = SearchContext.finders[how.to_sym]
        raise ArgumentError, "cannot find elements by #{how.inspect}" unless by

        bridge.find_elements_by by, what, ref
      end

      private

      def extract_args(args)
        case args.size
        when 2
          args
        when 1
          arg = args.first

          unless arg.respond_to?(:shift)
            raise ArgumentError,
                  "expected #{arg.inspect}:#{arg.class} to respond to #shift"
          end

          # this will be a single-entry hash, so use #shift over #first or #[]
          arr = arg.dup.shift
          raise ArgumentError, "expected #{arr.inspect} to have 2 elements" unless arr.size == 2

          arr
        else
          raise ArgumentError, "wrong number of arguments (#{args.size} for 2)"
        end
      end
    end # SearchContext
  end # WebDriver
end # Selenium

View on GitHub (pinned to aa36b38e69)

Solutions

  1. Pass two arguments: find_element(:css, '#my-id').
  2. Or pass a single Hash: find_element(css: '#my-id').
  3. Check that the variable holding your locator options is not nil before calling find_element.

Example fix

// before
element = driver.find_element(:css)  # missing the locator value

// after
element = driver.find_element(:css, '#submit')
Defensive patterns

Strategy: type-guard

Validate before calling

def safe_find(driver, *args)
  if args.size == 1 && !args.first.respond_to?(:shift)
    raise ArgumentError, "Single argument must be a Hash or Array, got #{args.first.class}"
  end
  driver.find_element(*args)
end

Type guard

def valid_find_arg?(arg)
  arg.respond_to?(:shift)
end

Try / catch

begin
  driver.find_element(locator)
rescue ArgumentError => e
  raise unless e.message.include?('to respond to #shift')
  # locator is not a Hash; wrap it
  driver.find_element(*Array(locator))
end

Prevention

When it happens

Trigger: Calling find_element('css') with only a strategy and no locator value. Calling find_element(42) or find_element(nil) with a non-Hash, non-Array argument. Calling find_element(SomeObject.new) with an arbitrary object.

Common situations: Forgot the second argument (the locator value). Passing a driver or element object as the sole argument by mistake. Variable interpolation producing nil where a Hash was expected.

Related errors


AI-assisted analysis of SeleniumHQ/selenium@aa36b38e69 (2026-08-14). Data as JSON: /api/errors/7b075cd7fdcbbfba. Report an issue: GitHub.