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)}&"
endView on GitHub (pinned to b25b1b26cc)
Solutions
- Pass a Hash of params: conn.get('/search', q: 'ruby', filters: { lang: 'en' }) — nesting is what this encoder is for.
- If the query string is already built, append it to the URL: conn.get("/search?#{qs}") and pass no params argument.
- For Array inputs, give [key, value] pairs: conn.get(url, [[:a, 1], [:b, 2]]) — Arrays skip sorting to preserve order.
- 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
- Remember the nested encoder is the default: conn.get(url, params) encodes params, it does not append a raw string.
- Keep argument orders straight — get(url, params) vs post(url, body) — review when porting between HTTP clients.
- For objects without #to_hash, convert with .to_h at the call site before passing.
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
- Can't convert #{params.class} into Hash.
- expected #{value_type.name} (got #{context[subkey].class.nam
- exceeded nested parameter depth limit of #{@param_depth_limi
- Expected :read, :write, :open. Got #{type.inspect} :(
- no stubbed request for #{env[:method]} #{env[:url]} #{env[:b
AI-assisted analysis of lostisland/faraday@b25b1b26cc (2026-08-21).
Data as JSON: /api/errors/a6b24f20e33776f3.
Report an issue: GitHub.