lostisland/faraday · error · Faraday::Error

exceeded nested parameter depth limit of #{@param_depth_limi

Error message

exceeded nested parameter depth limit of #{@param_depth_limit}

What it means

NestedParamsEncoder#decode guards its recursion with a depth counter: validate_params_depth! raises Faraday::Error once nesting exceeds @param_depth_limit (default 100). This is a stack-exhaustion/DoS protection for adversarial query strings like a[b][c][d]... with hundreds of levels, mirroring similar limits in Rack's query parser.

Source

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

                         "(got #{context[subkey].class.name}) for param `#{subkey}'"
      end

      context[subkey] ||= value_type.new
    end

    def match_context(context, subkey)
      context << {} if !context.last.is_a?(Hash) || context.last.key?(subkey)
      context.last
    end

    def add_to_context(is_array, context, value, subkey)
      is_array ? context << value : context[subkey] = value
    end

    def validate_params_depth!(depth)
      return unless @param_depth_limit && depth > @param_depth_limit

      raise Faraday::Error, "exceeded nested parameter depth limit of #{@param_depth_limit}"
    end

    # Internal: convert a nested hash with purely numeric keys into an array.
    # FIXME: this is not compatible with Rack::Utils.parse_nested_query
    # @!visibility private
    def dehash(hash, depth)
      hash.each do |key, value|
        hash[key] = dehash(value, depth + 1) if value.is_a?(Hash)
      end

      if depth.positive? && !hash.empty? && hash.keys.all? { |k| k =~ /^\d+$/ }
        hash.sort.map(&:last)
      else
        hash
      end
    end
  end

View on GitHub (pinned to b25b1b26cc)

Solutions

  1. Rescue Faraday::Error at the parse boundary and reject the request (400) — for untrusted input the limit doing its job is the correct outcome.
  2. If your data legitimately nests deeper, raise the ceiling at boot: Faraday::NestedParamsEncoder.param_depth_limit = 250 (it is an attr_accessor on the encoder module).
  3. Prefer sending deep structures as a JSON request body rather than nested query params.
  4. For untrusted query strings that need no nesting, switch to Faraday::FlatParamsEncoder, which never recurses and cannot hit the limit.

Example fix

# before
Faraday::Utils.parse_query('a' + '[b]' * 150 + '=1')
# => Faraday::Error: exceeded nested parameter depth limit of 100

# after (untrusted input: reject instead of parse)
begin
  params = Faraday::Utils.parse_query(raw_query)
rescue Faraday::Error
  return [400, {}, ['query too deeply nested']]
end

# after (trusted deep data: raise the ceiling once, at boot)
Faraday::NestedParamsEncoder.param_depth_limit = 250
Defensive patterns

Strategy: try-catch

Validate before calling

return [400, {}, ['query too deep']] if raw_query.count('[') >= Faraday::NestedParamsEncoder.param_depth_limit
params = Faraday::NestedParamsEncoder.decode(raw_query)

Type guard

def within_depth_limit?(raw_query)
  raw_query.count('[') < (Faraday::NestedParamsEncoder.param_depth_limit || Float::INFINITY)
end

Try / catch

begin
  params = Faraday::NestedParamsEncoder.decode(raw_query)
rescue Faraday::Error => e
  raise unless e.message.include?('depth limit')
  params = Faraday::FlatParamsEncoder.decode(raw_query) # or reject: [400, {}, []]
end

Prevention

When it happens

Trigger: Decoding a query string whose bracket nesting exceeds 100 levels, e.g. 'a' + '[b]' * 101 + '=1'; legitimately deep structures only if your own encoder emitted them (hashes nested >100 deep serialized to a query string). Triggered wherever Faraday parses untrusted URLs: env[:params] decoding in adapters, Faraday::Utils.parse_query with the nested decoder.

Common situations: A public endpoint or middleware that parses attacker-controlled URLs receiving scanner traffic with deeply nested keys (classic DoS probe); replaying captured logs; rare apps that serialize genuinely deep object trees into query strings instead of a JSON body.

Related errors


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