ruby-grape/grape · error · ArgumentError
coerce_with disallowed for type: JSON
Error message
coerce_with disallowed for type: JSON
What it means
The `JSON` type (and `Array[JSON]`, listed in ParamsScope::SPECIAL_JSON) is special: Grape already defines its own coercion for it by parsing the incoming string with the configured JSON backend. Supplying `coerce_with` for a JSON type would be redundant and ambiguous, so `check_coerce_with` raises ArgumentError at declaration time.
Source
Thrown at lib/grape/validations/params_scope.rb:375
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
# with the actual type value.
return unless coerce_options.type
View on GitHub (pinned to 22d7975629)
Solutions
- Remove `coerce_with` — `type: JSON` already parses the request value
- Keep custom parsing under a different type: `requires :payload, type: String, coerce_with: ->(raw) { Oj.load(raw) }`
- For structured custom types, use a custom type class with a class-level `parse` and pass it as `type:`
Example fix
# before
requires :payload, type: JSON, coerce_with: ->(raw) { Oj.load(raw) }
# after
requires :payload, type: JSON Defensive patterns
Strategy: validation
Validate before calling
SPECIAL_JSON = [Grape::Validations::Types::JSON, Array[Grape::Validations::Types::JSON]].freeze def assert_json_without_coercer!(opts) return unless opts[:coerce_with] && SPECIAL_JSON.include?(opts[:type]) raise ArgumentError, 'do not pass coerce_with with JSON types; the type defines its own coercion' end
Try / catch
begin
requires :payload, type: JSON, coerce_with: ->(raw) { Oj.load(raw) }
rescue ArgumentError => e
raise "JSON param must not carry coerce_with: #{e.message}"
end Prevention
- Remember that `JSON` and `Array[JSON]` come with built-in parsing — never pair them with `coerce_with`
- Keep custom parsing under `type: String` or a custom type class
- Configure the app-wide JSON backend instead of per-param parsing overrides
When it happens
Trigger: `requires :payload, type: JSON, coerce_with: ->(raw) { Oj.load(raw) }`; `requires :rows, type: Array[JSON], coerce_with: CustomParser`; any declaration whose resolved coercion type is `JSON` or `Array[JSON]` while a `coerce_with` method is present.
Common situations: Switching a `type: String` param with custom parsing over to `type: JSON` and forgetting to remove `coerce_with`; trying to plug a faster parser (Oj) per-param instead of configuring Grape's JSON backend globally.
Related errors
- must supply type for coerce_with
- oneof: requires type: Hash
- oneof: must be a non-empty Array of blocks
- oneof: each variant must be a Proc
- Unknown type: #{type}
AI-assisted analysis of ruby-grape/grape@22d7975629 (2026-08-21).
Data as JSON: /api/errors/48baca3cc6f270fa.
Report an issue: GitHub.