{"record":{"id":"e7fa4d02c6b422b0","repo":"ruby-grape/grape","slug":"type-type-should-support-coercion-via","errorCode":null,"errorMessage":"type #{type} should support coercion via `[]`","messagePattern":"type #(.+?) should support coercion via `\\[\\]`","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/grape/dry_types.rb","lineNumber":51,"sourceCode":"        TrueClass => DryTypes::Params::Bool.constrained(eql: true),\n        FalseClass => DryTypes::Params::Bool.constrained(eql: false),\n        String => DryTypes::Coercible::String\n      }.freeze\n\n      def initialize\n        super\n        @cache = Hash.new do |h, params_type|\n          h[params_type] = MAPPING.fetch(params_type) do\n            DryTypes.wrapped_dry_types_const_get(DryTypes::Params, params_type)\n          end\n        end\n      end\n    end\n\n    def self.wrapped_dry_types_const_get(dry_type, type)\n      dry_type.const_get(type.name, false)\n    rescue NameError\n      raise ArgumentError, \"type #{type} should support coercion via `[]`\" unless type.respond_to?(:[])\n    end\n  end\nend\n","sourceCodeStart":33,"sourceCodeEnd":55,"githubUrl":"https://github.com/ruby-grape/grape/blob/22d7975629846a3c0c7bd2b34e140a7a1b4af8f6/lib/grape/dry_types.rb#L33-L55","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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).","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?`).","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.","If the type should exist (e.g. Params::Symbol), upgrade dry-types to a version that defines it."],"exampleFix":"# before\nparams do\n  requires :data, type: MyCustomValueObject # raises ArgumentError\nend\n\n# after\nparams do\n  requires :data, type: String\nend\n\n# or make the custom type coercible\nclass MyCustomValueObject\n  def self.[](raw) = parse(raw)\n  def self.parse(raw) = new(raw)\nend","handlingStrategy":"type-guard","validationCode":"SUPPORTED = { Grape::API::Boolean, BigDecimal, Numeric, TrueClass, FalseClass, String, Integer, Float, Date, Time, DateTime, Array, Hash, Set }.freeze\n\ndef coercible?(type)\n  SUPPORTED.include?(type) || type.respond_to?(:[]) || type.is_a?(Dry::Types::Type)\nend\n\ncoercible?(MyCustomValueObject) or raise \"#{MyCustomValueObject} cannot coerce params\"","typeGuard":"def grape_coercible_type?(type)\n  return true if type.respond_to?(:[])\n  return true if type.is_a?(Dry::Types::Type)\n\n  Dry::Types::Params.const_defined?(type.name.to_s, false)\nrescue NameError\n  false\nend","tryCatchPattern":null,"preventionTips":["Restrict `type:` declarations to built-in primitives, Grape special types, or dry-type objects.","Cover param definitions with a boot-time smoke spec that mounts every endpoint so definition-time ArgumentErrors fail CI, not production.","For custom types, implement `self.[]` (or `parse`/`parsed?`) and unit-test it before referencing it in params."],"tags":["grape","dry-types","params","coercion","type-declaration"],"backgroundTag":"invalid-parameter-type","analyzedSha":"22d7975629846a3c0c7bd2b34e140a7a1b4af8f6","analyzedAt":"2026-08-21T17:03:54.627Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}