jnunemaker/httparty · error · ArgumentError
Headers must be an object which responds to #to_hash
Error message
Headers must be an object which responds to #to_hash
What it means
The class-level DSL method `headers` raises ArgumentError when given a value that does not respond to #to_hash. headers(h) merges the argument into default_options[:headers] for every request, so it must be a Hash or a Hash-like object; the guard fires at class-definition time, before any network traffic happens.
Source
Thrown at lib/httparty.rb:243
# The output stream is passed on to Net::HTTP#set_debug_output.
#
# class Foo
# include HTTParty
# debug_output $stderr
# end
def debug_output(stream = $stderr)
default_options[:debug_output] = stream
end
# Allows setting HTTP headers to be used for each request.
#
# class Foo
# include HTTParty
# headers 'Accept' => 'text/html'
# end
def headers(h = nil)
if h
raise ArgumentError, 'Headers must be an object which responds to #to_hash' unless h.respond_to?(:to_hash)
default_options[:headers] ||= {}
default_options[:headers].merge!(h.to_hash)
else
default_options[:headers] || {}
end
end
def cookies(h = {})
raise ArgumentError, 'Cookies must be an object which responds to #to_hash' unless h.respond_to?(:to_hash)
default_cookies.add_cookies(h)
end
# Proceed to the location header when an HTTP response dictates a redirect.
# Redirects are always followed by default.
#
# @example
# class Foo
# include HTTPartyView on GitHub (pinned to 8f4a09e343)
Solutions
- Pass a Hash with header names as keys: `headers 'Accept' => 'text/html'`.
- Parse external header strings before the call: `headers Hash[raw.split(': ').map ...]` or store config as YAML/JSON and parse it to a Hash.
- For one-off headers, use the per-request option instead: `Foo.get(url, headers: { 'Accept' => 'text/html' })`.
Example fix
# before headers 'Accept: application/json' # String -> ArgumentError # after headers 'Accept' => 'application/json'
Defensive patterns
Strategy: validation
Validate before calling
raise ArgumentError, 'headers config must be a Hash' unless h.respond_to?(:to_hash) headers h.to_hash
Type guard
hash_like = ->(v) { v.respond_to?(:to_hash) } Try / catch
begin
headers h
rescue ArgumentError
raise ConfigError, 'headers must be configured as a Hash, e.g. { "Accept" => "application/json" }'
end Prevention
- Never paste raw 'Key: value' curl strings into the headers DSL.
- Keep header config in YAML/JSON parsed to a Hash at load time.
- Use per-request headers: hash for dynamic values.
When it happens
Trigger: `headers 'Accept: text/html'` (a raw header string instead of a Hash), `headers nil` passed explicitly (h is truthy-checked, but a non-hash truthy value raises), `headers JSON.generate(...)`, or `headers [[:accept, 'text/html']]` inside an HTTParty class.
Common situations: Copy-pasting a curl -H 'X: y' style string into the DSL, feeding headers read from a config file or ENV variable that arrives as a String, and mixing up this setter with the request-level `get url, headers: {...}` option.
Understand the failure class
Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.
Related errors
- Default params must be an object which responds to #to_hash
- Cookies must be an object which responds to #to_hash
- The URI adapter should respond to #parse
- #{ timeout_type } must be an integer or float
- :headers must be a hash
AI-assisted analysis of jnunemaker/httparty@8f4a09e343 (2026-08-21).
Data as JSON: /api/errors/89ead00fbcdea5a6.
Report an issue: GitHub.