SeleniumHQ/selenium · error · ArgumentError
:capabilities parameter only accepts objects responding to #
Error message
:capabilities parameter only accepts objects responding to #as_json which #{cap.class} does not What it means
Raised by Remote::Driver#generate_capabilities when an element of the :capabilities array does not respond to #as_json. The method iterates the array, resolves Symbols to Options via WebDriver::Options.send(cap), and calls cap.as_json on everything else to produce the JSON capabilities payload; objects without as_json cannot be serialized for the W3C new-session request.
Source
Thrown at rb/lib/selenium/webdriver/remote/driver.rb:85
end
def process_options(options, capabilities)
if options && capabilities
msg = "Don't use both :options and :capabilities when initializing #{self.class}, prefer :options"
raise ArgumentError, msg
elsif options.nil? && capabilities.nil?
raise ArgumentError, "#{self.class} needs :options to be set"
end
options ? options.as_json : generate_capabilities(capabilities)
end
def generate_capabilities(capabilities)
Array(capabilities).map { |cap|
if cap.is_a? Symbol
cap = WebDriver::Options.send(cap)
elsif !cap.respond_to? :as_json
msg = ":capabilities parameter only accepts objects responding to #as_json which #{cap.class} does not"
raise ArgumentError, msg
end
cap.as_json
}.inject(:merge)
end
end # Driver
end # Remote
end # WebDriver
end # Selenium
View on GitHub (pinned to aa36b38e69)
Solutions
- Use Symbols that map to Options: capabilities: [:chrome] (resolved via WebDriver::Options.send).
- Use Options or Capabilities objects that implement as_json: capabilities: [Selenium::WebDriver::Options.chrome].
- If passing custom objects, ensure they implement a valid #as_json method returning a Hash.
- Migrate to the :options parameter which is type-checked and clearer.
Example fix
# before
driver = Selenium::WebDriver.for(:remote, url: url,
capabilities: [{browserName: 'chrome'}]) # raw hash may fail depending on JSON gem
# after
driver = Selenium::WebDriver.for(:remote, url: url,
options: Selenium::WebDriver::Options.chrome) Defensive patterns
Strategy: type-guard
Validate before calling
# Validate each capability object before passing:
caps_array.each do |cap|
next if cap.is_a?(Symbol)
raise ArgumentError, "#{cap.class} does not respond to #as_json" unless cap.respond_to?(:as_json)
end Type guard
def valid_capabilities_array?(arr)
arr.all? { |c| c.is_a?(Symbol) || c.respond_to?(:as_json) }
end Try / catch
begin Selenium::WebDriver.for(:remote, url: url, capabilities: caps) rescue ArgumentError # fall back to :options with a single Options object Selenium::WebDriver.for(:remote, url: url, options: Selenium::WebDriver::Options.chrome) end
Prevention
- Migrate from :capabilities arrays to the single :options parameter.
- If using :capabilities, only pass Symbols or Options/Capabilities objects.
- Do not put raw Hashes or Strings in the capabilities array.
When it happens
Trigger: Passing capabilities: [some_string] or capabilities: [an_integer]. Passing a raw Hash inside the array (Hash does respond to as_json via the JSON gem, so this typically triggers for truly non-serializable objects). Passing a custom class instance that doesn't implement as_json. Passing a Capabilities object from an incompatible library version.
Common situations: Using the legacy :capabilities array API with objects that were valid in Selenium 3 but whose as_json contract changed. Passing driver-specific config objects instead of Options/Capabilities. Mixed-up capability arrays containing strings parsed from config files.
Related errors
- These options are not w3c compliant: #{options}
- argument should be a Hash or implement #capabilities
- expected String or Symbol, got #{key.inspect}:#{key.class}
- Don't use both :options and :capabilities when initializing
- Safari does not support options that are not namespaced
AI-assisted analysis of SeleniumHQ/selenium@aa36b38e69 (2026-08-14).
Data as JSON: /api/errors/6595c620ff5557be.
Report an issue: GitHub.