SeleniumHQ/selenium · error · TypeError

expected String or Symbol, got #{key.inspect}:#{key.class}

Error message

expected String or Symbol, got #{key.inspect}:#{key.class}

What it means

Raised as a TypeError by Capabilities#convert_key when a capability key is neither a String nor a Symbol. convert_key normalizes keys to camelCase strings for the W3C/JSON wire protocol; String keys pass through as-is, Symbol keys are camel-cased, and any other type (Integer, Array, nil used as key) is rejected because it cannot form a valid capability name.

Source

Thrown at rb/lib/selenium/webdriver/remote/capabilities.rb:252

          when Hash
            value.each_with_object({}) do |(k, v), h|
              h[convert_key(k)] = process_capabilities(k, v, h)
            end
          when Capabilities, Options
            value.as_json
          else
            convert_value(key, value)
          end
        end

        def convert_key(key)
          case key
          when String
            key.to_s
          when Symbol
            self.class.camel_case(key)
          else
            raise TypeError, "expected String or Symbol, got #{key.inspect}:#{key.class}"
          end
        end

        def convert_value(key, value)
          case key
          when :platform
            value.to_s.upcase
          when :proxy
            value&.as_json
          when :unhandled_prompt_behavior
            value.is_a?(Symbol) ? value.to_s.tr('_', ' ') : value
          else
            value
          end
        end
      end # Capabilities
    end # Remote
  end # WebDriver

View on GitHub (pinned to aa36b38e69)

Solutions

  1. Ensure all capability keys are Symbols (preferred) or Strings: caps[:browserName] = 'chrome'.
  2. When loading from external data, coerce keys: external.each { |k, v| caps[k.to_sym] = v }.
  3. Filter out non-string/symbol keys before assignment if the source data is untrusted or mixed.

Example fix

# before
config.each { |k, v| caps[k] = v }  # k may be Integer

# after
config.each { |k, v| caps[k.to_sym] = v if k.respond_to?(:to_sym) }
Defensive patterns

Strategy: validation

Validate before calling

# Coerce keys to Symbol before assignment:
safe_keys = external_hash.select { |k, _| k.is_a?(String) || k.is_a?(Symbol) }
safe_keys.each { |k, v| capabilities[k.to_sym] = v }

Type guard

def valid_capability_key?(key)
  key.is_a?(String) || key.is_a?(Symbol)
end

Try / catch

begin
  capabilities[key] = value
rescue TypeError
  capabilities[key.to_sym] = value if key.respond_to?(:to_sym)
end

Prevention

When it happens

Trigger: Using an Integer as a key: caps[0] = 'value' followed by serialization. Passing a hash with non-string/symbol keys via the Capabilities constructor. Iterating over external data (e.g. parsed JSON with integer keys from a numeric-indexed structure) and assigning each to capabilities. Using a Class or Module object as a key.

Common situations: Loading capability overrides from YAML/JSON where keys are deserialized as non-strings in edge cases. Programmatic hash building that accidentally uses an integer index. Passing a Data/Struct field name object instead of a symbol. Refactoring from indifferent-access hashes that exposed integer lookups.

Related errors


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