ruby-grape/grape · error · ArgumentError

must supply type for coerce_with

Error message

must supply type for coerce_with

What it means

Grape requires every `coerce_with:` option on `requires`/`optional` to be paired with an explicit `type:`. `coerce_with` only says HOW the incoming param is converted; `type:` says WHAT the param becomes and drives later validation, defaults, and documentation. When `ParamsScope#check_coerce_with` finds a coercion method but no type, it raises ArgumentError while the API class body is being evaluated, before any request is served.

Source

Thrown at lib/grape/validations/params_scope.rb:372

        # Presence runs first — `required` is forwarded to every subsequent
        # validator (some short-circuit on it).
        validate_presence(spec, attrs)

        # Coerce runs second — later validators see the typed value.
        validate_coerce(spec, attrs)

        spec.validator_entries.each do |type, options|
          validate(type, options, attrs, spec.required?, spec.shared_opts)
        end
      end

      # Enforce correct usage of :coerce_with on a CoerceOptions.
      # We do not allow coercion without a type, nor with +JSON+ as a type
      # since that defines its own coercion method.
      def check_coerce_with(coerce_options)
        return unless coerce_options.coerce_method
        raise ArgumentError, 'must supply type for coerce_with' unless coerce_options.type
        return unless SPECIAL_JSON.include?(coerce_options.type)

        raise ArgumentError, 'coerce_with disallowed for type: JSON'
      end

      def validate_presence(spec, attrs)
        return unless spec.required?

        validate('presence', spec.presence_options, attrs, true, spec.shared_opts)
      end

      def validate_coerce(spec, attrs)
        coerce_options = spec.coerce_options
        check_coerce_with(coerce_options)
        # Falsy check is intentional: when a remountable API is first evaluated
        # on its base instance (no configuration supplied yet),
        # configuration[:some_type] evaluates to nil. Skipping instantiation
        # here is correct — the real mounted instance will replay this step

View on GitHub (pinned to 22d7975629)

Solutions

  1. Add `type:` to the same declaration, e.g. `requires :age, type: Integer, coerce_with: ->(v) { v.to_i }`
  2. If the conversion belongs to a custom class, give the class a class-level `parse` method and pass it as `type:` alone (Grape custom type), dropping `coerce_with`
  3. If declarations are generated dynamically, assert that any hash containing `:coerce_with` also contains `:type` or `:types` before it reaches the DSL

Example fix

# before
requires :age, coerce_with: ->(v) { v.to_i }

# after
requires :age, type: Integer, coerce_with: ->(v) { v.to_i }
Defensive patterns

Strategy: validation

Validate before calling

def assert_coerce_declaration!(opts)
  return unless opts.key?(:coerce_with) && !opts.key?(:type) && !opts.key?(:types)

  raise ArgumentError, ':coerce_with must be paired with :type (keys given: ' + opts.keys.inspect + ')'
end

assert_coerce_declaration!(type: Integer, coerce_with: ->(v) { v.to_i })

Try / catch

begin
  MyAPI = Class.new(Grape::API) do
    requires :age, coerce_with: ->(v) { v.to_i } # raises during class definition
  end
rescue ArgumentError => e
  raise "Invalid param declaration while loading MyAPI: #{e.message}"
end

Prevention

When it happens

Trigger: Declaring `requires :age, coerce_with: ->(v) { v.to_i }` (proc with no type); `optional :token, coerce_with: CustomParser` (object responding to call/parse with no type); any requires/optional declaration whose options hash reaches ParamsScope with a `:coerce_with` key and no `:type`/`:types` key.

Common situations: Porting Sinatra-style endpoints and assuming the coercion proc's return value implies the type; copying example snippets that omit `type:`; building declarations from dynamically merged option hashes where the `:type` key is dropped or renamed.

Related errors


AI-assisted analysis of ruby-grape/grape@22d7975629 (2026-08-21). Data as JSON: /api/errors/da6381b7e49dcec1. Report an issue: GitHub.