{"record":{"id":"48baca3cc6f270fa","repo":"ruby-grape/grape","slug":"coerce-with-disallowed-for-type-json","errorCode":null,"errorMessage":"coerce_with disallowed for type: JSON","messagePattern":"coerce_with disallowed for type: JSON","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/grape/validations/params_scope.rb","lineNumber":375,"sourceCode":"        validate_presence(spec, attrs)\n\n        # Coerce runs second — later validators see the typed value.\n        validate_coerce(spec, attrs)\n\n        spec.validator_entries.each do |type, options|\n          validate(type, options, attrs, spec.required?, spec.shared_opts)\n        end\n      end\n\n      # Enforce correct usage of :coerce_with on a CoerceOptions.\n      # We do not allow coercion without a type, nor with +JSON+ as a type\n      # since that defines its own coercion method.\n      def check_coerce_with(coerce_options)\n        return unless coerce_options.coerce_method\n        raise ArgumentError, 'must supply type for coerce_with' unless coerce_options.type\n        return unless SPECIAL_JSON.include?(coerce_options.type)\n\n        raise ArgumentError, 'coerce_with disallowed for type: JSON'\n      end\n\n      def validate_presence(spec, attrs)\n        return unless spec.required?\n\n        validate('presence', spec.presence_options, attrs, true, spec.shared_opts)\n      end\n\n      def validate_coerce(spec, attrs)\n        coerce_options = spec.coerce_options\n        check_coerce_with(coerce_options)\n        # Falsy check is intentional: when a remountable API is first evaluated\n        # on its base instance (no configuration supplied yet),\n        # configuration[:some_type] evaluates to nil. Skipping instantiation\n        # here is correct — the real mounted instance will replay this step\n        # with the actual type value.\n        return unless coerce_options.type\n","sourceCodeStart":357,"sourceCodeEnd":393,"githubUrl":"https://github.com/ruby-grape/grape/blob/22d7975629846a3c0c7bd2b34e140a7a1b4af8f6/lib/grape/validations/params_scope.rb#L357-L393","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","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:`"],"exampleFix":"# before\nrequires :payload, type: JSON, coerce_with: ->(raw) { Oj.load(raw) }\n\n# after\nrequires :payload, type: JSON","handlingStrategy":"validation","validationCode":"SPECIAL_JSON = [Grape::Validations::Types::JSON, Array[Grape::Validations::Types::JSON]].freeze\n\ndef assert_json_without_coercer!(opts)\n  return unless opts[:coerce_with] && SPECIAL_JSON.include?(opts[:type])\n\n  raise ArgumentError, 'do not pass coerce_with with JSON types; the type defines its own coercion'\nend","typeGuard":null,"tryCatchPattern":"begin\n  requires :payload, type: JSON, coerce_with: ->(raw) { Oj.load(raw) }\nrescue ArgumentError => e\n  raise \"JSON param must not carry coerce_with: #{e.message}\"\nend","preventionTips":["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"],"tags":["grape","params","coercion","json","argumenterror","dsl"],"backgroundTag":"conflicting-options","analyzedSha":"22d7975629846a3c0c7bd2b34e140a7a1b4af8f6","analyzedAt":"2026-08-21T17:03:54.627Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}