lostisland/faraday · error · TypeError

Can't convert #{params.class} into Hash.

Error message

Can't convert #{params.class} into Hash.

What it means

Faraday::NestedParamsEncoder.encode serializes params into Rails-style nested query strings (a[b]=c). Like the flat encoder it accepts nil, an Array of pairs, or anything responding to #to_hash, and raises TypeError for all other inputs. The nested encoder is Faraday's default for encoding, so this error surfaces on ordinary conn.get(url, params) calls with a bad params object.

Source

Thrown at lib/faraday/encoders/nested_params_encoder.rb:16

# frozen_string_literal: true

module Faraday
  # Sub-module for encoding parameters into query-string.
  module EncodeMethods
    # @param params [nil, Array, #to_hash] parameters to be encoded
    #
    # @return [String] the encoded params
    #
    # @raise [TypeError] if params can not be converted to a Hash
    def encode(params)
      return nil if params.nil?

      unless params.is_a?(Array)
        unless params.respond_to?(:to_hash)
          raise TypeError, "Can't convert #{params.class} into Hash."
        end

        params = params.to_hash
        params = params.map do |key, value|
          key = key.to_s if key.is_a?(Symbol)
          [key, value]
        end

        # Only to be used for non-Array inputs. Arrays should preserve order.
        params.sort! if @sort_params
      end

      # The params have form [['key1', 'value1'], ['key2', 'value2']].
      buffer = +''
      params.each do |parent, value|
        encoded_parent = escape(parent)
        buffer << "#{encode_pair(encoded_parent, value)}&"
      end

View on GitHub (pinned to b25b1b26cc)

Solutions

  1. Pass a Hash of params: conn.get('/search', q: 'ruby', filters: { lang: 'en' }) — nesting is what this encoder is for.
  2. If the query string is already built, append it to the URL: conn.get("/search?#{qs}") and pass no params argument.
  3. For Array inputs, give [key, value] pairs: conn.get(url, [[:a, 1], [:b, 2]]) — Arrays skip sorting to preserve order.
  4. Convert exotic objects on the caller side with .to_h, or implement #to_hash on the class.

Example fix

# before
conn.get('/search', 'q=ruby&lang=en')
# => TypeError: Can't convert String into Hash.

# after
conn.get('/search', q: 'ruby', lang: 'en')
# equivalent nested form:
conn.get('/search', q: 'ruby', filters: { lang: 'en' }) # /search?q=ruby&filters[lang]=en
Defensive patterns

Strategy: type-guard

Validate before calling

raise TypeError, 'params must be a Hash' unless params.is_a?(Hash)
conn.get(url, params)

Type guard

def encodable_params?(params)
  params.nil? || params.is_a?(Array) || params.respond_to?(:to_hash)
end

Try / catch

begin
  conn.get(url, params)
rescue TypeError => e
  raise unless e.message.include?('into Hash')
  conn.get("#{url}?#{URI.encode_www_form(params)}") if params.is_a?(String)
end

Prevention

When it happens

Trigger: Passing a pre-encoded query String as params: conn.post('/items', 'a=1&b=2') — the second positional argument of post is the body, but with get it is params, and both reach an encoder; passing an Integer, Symbol or custom object that has no #to_hash; feeding a JSON payload string into params by mistake.

Common situations: Confusing the argument order of conn.get(url, params) with conn.post(url, body); assuming params accepts a query string like URI.encode_www_form output; migrating from other HTTP clients whose param arguments are strings; sending a StringIO or IO-like object as params.

Related errors


AI-assisted analysis of lostisland/faraday@b25b1b26cc (2026-08-21). Data as JSON: /api/errors/a6b24f20e33776f3. Report an issue: GitHub.