ruby-grape/grape · error · ArgumentError

Unknown type: #{type}

Error message

Unknown type: #{type}

What it means

When Grape builds a param coercer it accepts a Class (primitives like Integer/String, Grape special types, custom types with `parse`), an Array or Set type specifier (`[Integer]`, `Set[Integer]`), or an Array of classes for unions. Anything else — a Symbol, a String, a dry-types type object, or an instance instead of a class — falls through to `DryTypeCoercer.collection_coercer_for`, which only knows Array and Set and raises `ArgumentError: Unknown type: <value>` at API-definition time.

Source

Thrown at lib/grape/validations/types/dry_type_coercer.rb:26

      # but check its type. More information there
      # https://dry-rb.org/gems/dry-types/main/built-in-types/
      class DryTypeCoercer
        extend Grape::Util::FreezeOnNew

        class << self
          # Returns a collection coercer which corresponds to a given type.
          # Example:
          #
          #    collection_coercer_for(Array)
          #    #=> Grape::Validations::Types::ArrayCoercer
          def collection_coercer_for(type)
            case type
            when Array
              ArrayCoercer
            when Set
              SetCoercer
            else
              raise ArgumentError, "Unknown type: #{type}"
            end
          end

          # Returns an instance of a coercer for a given type
          def coercer_instance_for(type, strict: false)
            klass = type.instance_of?(Class) ? PrimitiveCoercer : collection_coercer_for(type)
            klass.new(type, strict:)
          end
        end

        def initialize(type, strict: false)
          @type = type
          @strict = strict
          @cache_coercer = strict ? DryTypes::StrictCache : DryTypes::ParamsCache
        end

        # Coerces the given value to a type which was specified during
        # initialization as a type argument.

View on GitHub (pinned to 22d7975629)

Solutions

  1. Pass the class itself: `type: Integer`, `type: String`, `type: JSON`
  2. For custom value objects, pass the class and implement the Grape custom-type contract (class-level `parse`, optional `parsed?`)
  3. To reuse a dry-type, wrap it in a class with `def self.parse(val)` that delegates to the dry type, then use `type: YourWrapper`
  4. For collections use `[ElementType]` or `Set[ElementType]`

Example fix

# before
requires :user_id, type: Dry::Types::Params::Integer

# after
requires :user_id, type: Integer

# or wrap a dry-type as a custom type
class UserId
  def self.parse(val) = Dry::Types['coercible.integer'].call(val)
end
requires :user_id, type: UserId
Defensive patterns

Strategy: type-guard

Validate before calling

def supported_grape_type?(type)
  type.instance_of?(Class) || type.is_a?(Array) || type.is_a?(Set)
end

raise ArgumentError, "unsupported type #{type.inspect}" unless supported_grape_type?(decl[:type])

Type guard

def grape_supported_type?(type)
  type.instance_of?(Class) || type.is_a?(Array) || type.is_a?(Set)
end

# usage before the DSL:
# raise ArgumentError, "bad type #{t.inspect}" unless grape_supported_type?(t)

Try / catch

begin
  requires :user_id, type: type_from_config
rescue ArgumentError => e
  raise "type #{type_from_config.inspect} is not a Grape type (pass the class, not a symbol/instance/dry-type)"
end

Prevention

When it happens

Trigger: `requires :count, type: :integer` (symbol instead of class); `requires :n, type: 'Integer'` (string); `requires :t, type: Dry::Types::Params::Integer` (a dry-types object, not a Class); `requires :v, type: MyType.new` (instance instead of the class).

Common situations: Coming from Rails conventions where symbols name types; assuming Grape accepts dry-types objects directly under `type:`; passing the parsed instance because the custom-type docs show instances being returned from `parse`.

Related errors


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