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

  1. Read the message - it enumerates the only valid keys: body, headers, query, basic_auth
  2. Rename the misspelled key (header -> headers, params -> query, auth -> basic_auth, json_body -> body)
  3. Pass JSON payloads under body: as a Hash or JSON string, not json: or json_body:
  4. 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

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


AI-assisted analysis of bblimke/webmock@b187df8827 (2026-08-23). Data as JSON: /api/errors/59d610b5a549ec7b. Report an issue: GitHub.