jnunemaker/httparty · error · ArgumentError

:headers must be a hash

Error message

:headers must be a hash

What it means

Request#validate raises ArgumentError ':headers must be a hash' when the per-request options[:headers] is set but does not respond to #to_hash. This is the request-level twin of the class-level headers guard: values like a raw header string, an array of pairs, or an arbitrary object fail validation right before the request is performed (after any redirects that already consumed the limit would have raised first).

Source

Thrown at lib/httparty/request.rb:417

      response.get_fields('Set-Cookie').each { |cookie| cookies_hash.add_cookies(cookie) }

      options[:headers] ||= {}
      options[:headers]['Cookie'] = cookies_hash.to_cookie_string
    end

    # Uses the HTTP Content-Type header to determine the format of the
    # response It compares the MIME type returned to the types stored in the
    # SupportedFormats hash
    def format_from_mimetype(mimetype)
      if mimetype && parser.respond_to?(:format_from_mimetype)
        parser.format_from_mimetype(mimetype)
      end
    end

    def validate
      raise HTTParty::RedirectionTooDeep.new(last_response), 'HTTP redirects too deep' if options[:limit].to_i <= 0
      raise ArgumentError, 'only get, post, patch, put, delete, head, and options methods are supported' unless SupportedHTTPMethods.include?(http_method)
      raise ArgumentError, ':headers must be a hash' if options[:headers] && !options[:headers].respond_to?(:to_hash)
      raise ArgumentError, 'only one authentication method, :basic_auth or :digest_auth may be used at a time' if options[:basic_auth] && options[:digest_auth]
      raise ArgumentError, ':basic_auth must be a hash' if options[:basic_auth] && !options[:basic_auth].respond_to?(:to_hash)
      raise ArgumentError, ':digest_auth must be a hash' if options[:digest_auth] && !options[:digest_auth].respond_to?(:to_hash)
      raise ArgumentError, ':query must be hash if using HTTP Post' if post? && !options[:query].nil? && !options[:query].respond_to?(:to_hash)
    end

    def post?
      Net::HTTP::Post == http_method
    end

    def set_basic_auth_from_uri
      if path.userinfo
        username, password = path.userinfo.split(':')
        options[:basic_auth] = {username: username, password: password}
        @credentials_sent = true
      end
    end

View on GitHub (pinned to 8f4a09e343)

Solutions

  1. Pass a Hash: `Foo.get(url, headers: { 'Authorization' => "Bearer #{token}" })`.
  2. Coerce helper output: `headers: header_pairs.to_h`.
  3. Add a guard in shared request wrappers: `raise unless headers.respond_to?(:to_hash)`.

Example fix

# before
Foo.get(url, headers: "X-Api-Key: #{key}")   # String -> ArgumentError

# after
Foo.get(url, headers: { 'X-Api-Key' => key })
Defensive patterns

Strategy: validation

Validate before calling

headers = headers.to_hash if headers.respond_to?(:to_hash)
raise ArgumentError, ':headers must be a hash' unless headers.respond_to?(:to_hash)
Foo.get(url, headers: headers)

Type guard

hash_like = ->(v) { v.respond_to?(:to_hash) }

Try / catch

begin
  Foo.get(url, headers: hdrs)
rescue ArgumentError => e
  raise unless e.message.include?('headers')
  raise ArgumentError, "headers helper returned #{hdrs.class}; make it return a Hash"
end

Prevention

When it happens

Trigger: `Foo.get(url, headers: 'Authorization: Bearer x')`, `Foo.post(url, body: d, headers: JSON.generate(h))`, or building headers via `Array(h)` before passing them, e.g. when a helper method returns an array of ['Key','value'] pairs.

Common situations: Wrapping header construction in helpers that stringify for curl logging, copying header strings from API docs, and merging a Hash with something non-hash-like via `options[:headers] = maybe_hash` where maybe_hash is nil-replaced by a string default.

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


AI-assisted analysis of jnunemaker/httparty@8f4a09e343 (2026-08-21). Data as JSON: /api/errors/f7c3a6e1710a94fc. Report an issue: GitHub.