ruby-grape/grape · error · ArgumentError

type #{type} should support coercion via `[]`

Error message

type #{type} should support coercion via `[]`

What it means

When Grape maps a `params type:` declaration onto dry-types for coercion, it first checks a fixed MAPPING (Boolean, BigDecimal, Numeric, TrueClass, FalseClass, String) and then tries to find a constant with the same name under Dry::Types::Params (e.g. Params::Integer, Params::Date). If no such dry-type exists, Grape falls back to using the type itself for coercion, which requires the class to respond to `[]` the way Array, Hash, and Set do. When the type neither matches a dry-type constant nor responds to `[]`, Grape raises this ArgumentError at endpoint-definition time.

Source

Thrown at lib/grape/dry_types.rb:51

        TrueClass => DryTypes::Params::Bool.constrained(eql: true),
        FalseClass => DryTypes::Params::Bool.constrained(eql: false),
        String => DryTypes::Coercible::String
      }.freeze

      def initialize
        super
        @cache = Hash.new do |h, params_type|
          h[params_type] = MAPPING.fetch(params_type) do
            DryTypes.wrapped_dry_types_const_get(DryTypes::Params, params_type)
          end
        end
      end
    end

    def self.wrapped_dry_types_const_get(dry_type, type)
      dry_type.const_get(type.name, false)
    rescue NameError
      raise ArgumentError, "type #{type} should support coercion via `[]`" unless type.respond_to?(:[])
    end
  end
end

View on GitHub (pinned to 22d7975629)

Solutions

  1. Use a built-in primitive that dry-types supports (String, Integer, BigDecimal, Date, Time, etc.) or one of Grape's special types (Grape::API::Boolean, Numeric).
  2. For custom types, give the class a `self.[]` class method (like Array/Hash/Set) so Grape can coerce with it, or implement the custom-type contract (`parse`/`parsed?`).
  3. Pass an explicit dry-type object instead of a class, e.g. `requires :x, type: Dry.Types(:params)[:symbol]` or a Dry::Types::Params constant.
  4. If the type should exist (e.g. Params::Symbol), upgrade dry-types to a version that defines it.

Example fix

# before
params do
  requires :data, type: MyCustomValueObject # raises ArgumentError
end

# after
params do
  requires :data, type: String
end

# or make the custom type coercible
class MyCustomValueObject
  def self.[](raw) = parse(raw)
  def self.parse(raw) = new(raw)
end
Defensive patterns

Strategy: type-guard

Validate before calling

SUPPORTED = { Grape::API::Boolean, BigDecimal, Numeric, TrueClass, FalseClass, String, Integer, Float, Date, Time, DateTime, Array, Hash, Set }.freeze

def coercible?(type)
  SUPPORTED.include?(type) || type.respond_to?(:[]) || type.is_a?(Dry::Types::Type)
end

coercible?(MyCustomValueObject) or raise "#{MyCustomValueObject} cannot coerce params"

Type guard

def grape_coercible_type?(type)
  return true if type.respond_to?(:[])
  return true if type.is_a?(Dry::Types::Type)

  Dry::Types::Params.const_defined?(type.name.to_s, false)
rescue NameError
  false
end

Prevention

When it happens

Trigger: Declaring `params { requires :data, type: SomeCustomClass }` where SomeCustomClass is an application class with no class-level `[]` method. Passing `type: Symbol` or another primitive that the installed dry-types version does not expose under Dry::Types::Params. Passing a Module (e.g. `type: SomeNamespace`) instead of a class, which can never be const_get-ed on Dry::Types::Params.

Common situations: Upgrading grape or dry-types so a previously coerced type (e.g. Symbol on older dry-types) no longer has a Params constant. Using app-specific value objects as parameter types without implementing Grape's custom-type contract. Copying `type:` values from another codebase where a custom type was registered.

Related errors


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