bblimke/webmock · error · ArgumentError
Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(
Error message
Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')} What it means
WebMock validates the options hash passed to stub_request(...).with(...) (and to WebMock::RequestPattern.new) against a fixed whitelist: body, headers, query and basic_auth (lib/webmock/request_pattern.rb:59). Any other key raises ArgumentError, and the message itself enumerates the accepted keys. The guard exists to catch misspelled or unsupported matching options early, before a stub silently matches nothing.
Source
Thrown at lib/webmock/util/hash_validator.rb:14
# frozen_string_literal: true
module WebMock
class HashValidator
def initialize(hash)
@hash = hash
end
#This code is based on https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/hash/keys.rb
def validate_keys(*valid_keys)
valid_keys.flatten!
@hash.each_key do |k|
unless valid_keys.include?(k)
raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}")
end
end
end
end
end
View on GitHub (pinned to b187df8827)
Solutions
- Read the message - it enumerates the only valid keys: body, headers, query, basic_auth
- Rename the misspelled key (header -> headers, params -> query, auth -> basic_auth, json_body -> body)
- Pass JSON payloads under body: as a Hash or JSON string, not json: or json_body:
- For matching WebMock does not support, use the block form: .with { |req| req.headers['X-Token'] == 'abc' }
Example fix
// before
stub_request(:get, 'www.example.com').with(header: { 'Accept' => 'application/json' })
# => ArgumentError: Unknown key: header (valid: body, headers, query, basic_auth)
// after
stub_request(:get, 'www.example.com').with(headers: { 'Accept' => 'application/json' }) Defensive patterns
Strategy: validation
Validate before calling
VALID_WITH_KEYS = %w[body headers query basic_auth].freeze
options = { headers: { 'Accept' => 'application/json' } }
unknown = options.keys.map(&:to_s) - VALID_WITH_KEYS
raise ArgumentError, "unsupported .with keys: #{unknown.join(', ')}" if unknown.any?
stub_request(:get, 'www.example.com').with(options) Type guard
def valid_webmock_with_options?(opts) opts.is_a?(Hash) && (opts.keys.map(&:to_s) - %w[body headers query basic_auth]).empty? end
Try / catch
begin
stub_request(:get, 'www.example.com').with(options)
rescue ArgumentError => e
# e.message enumerates every valid key - re-raise with a hint pointing at typos
raise ArgumentError, "bad .with options (#{e.message}) - check header vs headers, params vs query"
end Prevention
- Memorize the four .with keys: body, headers, query, basic_auth
- Use the block form .with { |req| ... } for any matching not covered by the four keys
- Centralize stub option building in one helper so a typo fails in exactly one place
When it happens
Trigger: Calling .with() with a typo'd or unsupported key: .with(header: {...}) instead of headers:, .with(params: {...}) instead of query:, .with(json_body: ...) instead of body:, .with(auth: [...]) instead of basic_auth:. Also hit when building WebMock::RequestPattern.new(method, uri, options) directly with extra keys, or when a helper merges unrelated keys (e.g. :timeout) into the .with hash.
Common situations: Typos in the four option names; porting stubs from other libraries (FakeWeb/VCR style params or json options); assuming WebMock supports arbitrary matcher keys like :json or :timeout; programmatic option building where an unrelated key slips into the hash; examples copied from outdated blog posts.
Related errors
- #with method invoked with no arguments. Either options hash
- Wrong order. Use :before_local_stubs or :after_local_stubs
- Invalid notation. Must be one of: [:flat, :dot, :subscript,
- Key was repeated: #{key.inspect}
- Real HTTP connections are disabled. Unregistered request: #{
AI-assisted analysis of bblimke/webmock@b187df8827 (2026-08-23).
Data as JSON: /api/errors/59d610b5a549ec7b.
Report an issue: GitHub.