ruby-grape/grape · error · ArgumentError
:type may not be supplied with :types
Error message
:type may not be supplied with :types
What it means
`type:` declares a single coercion target; `types:` declares a union of allowed types. They are mutually exclusive — a param cannot be coerced to exactly one type and to several types at once — so `ValidationsSpec#initialize` raises ArgumentError the moment a `requires`/`optional` declaration carries both keys.
Source
Thrown at lib/grape/validations/validations_spec.rb:43
# Keys consumed by the spec itself; must NOT be dispatched as validators
# by the caller. Documentation-only keys are filtered through a separate
# set so that dual-purpose keys (length, default, values, except_values)
# aren't accidentally swallowed.
SPEC_CONSUMED_KEYS = %i[
type types coerce coerce_with coerce_message
presence message
fail_fast
desc description documentation
].freeze
attr_reader :raw, :coerce_type, :coerce_method, :coerce_message, :presence_options, :values, :except_values, :default, :allow_blank, :fail_fast, :shared_opts, :validator_entries
def self.from(validations)
new(validations)
end
def initialize(raw)
raise ArgumentError, ':type may not be supplied with :types' if raw.key?(:type) && raw.key?(:types)
@raw = raw
@coerce_type, @coerce_message, @coerce_method = parse_coerce(raw)
@values = resolve_value(raw[:values])
@except_values = resolve_value(raw[:except_values])
@default = raw[:default]
@presence_options = raw[:presence]
@allow_blank = resolve_value(raw[:allow_blank])
@fail_fast = raw[:fail_fast] || false
@guessed_coerce_type = guess_coerce_type(declared_coerce_type, @values, @except_values)
@shared_opts = { allow_blank: @allow_blank, fail_fast: @fail_fast }.freeze
@validator_entries = build_validator_entries(raw)
validate!
freeze
endView on GitHub (pinned to 22d7975629)
Solutions
- Delete `:type` and keep `types:` for union declarations
- When merging defaults, strip the stale key: `decl = defaults.merge(decl); decl.except!(:type) if decl.key?(:types)`
- Mount every endpoint in a spec so load-time declaration errors surface in CI rather than at production boot
Example fix
# before requires :id, type: Integer, types: [Integer, String] # after requires :id, types: [Integer, String]
Defensive patterns
Strategy: validation
Validate before calling
def assert_no_type_and_types!(opts) return unless opts.key?(:type) && opts.key?(:types) raise ArgumentError, ':type and :types are mutually exclusive (got both in ' + opts.inspect + ')' end
Try / catch
begin
requires :id, **merged_opts
rescue ArgumentError => e
raise "declaration carries both :type and :types after merging defaults: #{e.message}"
end Prevention
- Never merge a `type:`-bearing defaults hash into declarations that use `types:`
- Prefer a single canonical key in shared macros; normalize to `types:` at the boundary
- Mount every endpoint in a spec so mutually exclusive keys fail at load time in CI
When it happens
Trigger: `requires :id, type: Integer, types: [Integer, String]`; merging a shared defaults hash containing `type:` into a declaration that adds `types:`; generated declarations where both keys slip in.
Common situations: Refactoring a single-typed param into a union and forgetting to delete `type:`; `DEFAULT_OPTS = { type: String }.freeze` merged per-param with a declaration that also passes `types:`.
Related errors
- oneof: requires type: Hash
- oneof: must be a non-empty Array of blocks
- oneof: each variant must be a Proc
- must supply type for coerce_with
- coerce_with disallowed for type: JSON
AI-assisted analysis of ruby-grape/grape@22d7975629 (2026-08-21).
Data as JSON: /api/errors/90ce2aa6580ec367.
Report an issue: GitHub.