{"record":{"id":"07f7a46ab6a97ce0","repo":"ruby-grape/grape","slug":"oneof-each-variant-must-be-a-proc","errorCode":null,"errorMessage":"oneof: each variant must be a Proc","messagePattern":"oneof: each variant must be a Proc","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/grape/validations/params_scope.rb","lineNumber":408,"sourceCode":"        # here is correct — the real mounted instance will replay this step\n        # with the actual type value.\n        return unless coerce_options.type\n\n        validate('coerce', coerce_options, attrs, spec.required?, spec.shared_opts)\n      end\n\n      # Translate a `oneof: [proc, proc, ...]` declaration into a list of\n      # captured validator arrays — one array per variant. Each variant's\n      # block is evaluated in its own +ParamsScope+ backed by an\n      # {OneofCollector} so the full params DSL is available inside variants\n      # and the resulting validators are kept out of the real API's\n      # registration list.\n      def process_oneof!(validations)\n        raise ArgumentError, 'oneof: requires type: Hash' unless validations[:type] == Hash\n\n        variants = validations[:oneof]\n        raise ArgumentError, 'oneof: must be a non-empty Array of blocks' unless variants.is_a?(Array) && variants.any?\n        raise ArgumentError, 'oneof: each variant must be a Proc' unless variants.all?(Proc)\n\n        validations[:oneof] = variants.map { |block| OneofCollector.collect(block) }\n      end\n\n      def validate(type, options, attrs, required, opts)\n        validator_class = Validations.require_validator(type)\n        validator_instance = validator_class.new(\n          attrs,\n          options,\n          required,\n          self,\n          opts\n        )\n        @api.inheritable_setting.add_validation(validator_instance)\n      end\n\n      def all_element_blank?(scoped_params)\n        scoped_params.respond_to?(:all?) && scoped_params.all?(&:blank?)","sourceCodeStart":390,"sourceCodeEnd":426,"githubUrl":"https://github.com/ruby-grape/grape/blob/22d7975629846a3c0c7bd2b34e140a7a1b4af8f6/lib/grape/validations/params_scope.rb#L390-L426","documentation":"Each `oneof:` variant must be a Proc because Grape evaluates it in a scratch ParamsScope (OneofCollector) to capture its validators. Symbols, strings, hashes, or method names cannot be evaluated that way, so `variants.all?(Proc)` fails and ArgumentError is raised at definition time. Use `types:` for plain type unions.","triggerScenarios":"`oneof: [:by_name, :by_id]` (symbols); `oneof: ['name', 'id']` (strings); `oneof: [-> { requires :id }, { type: String }]` (mixed proc and hash).","commonSituations":"Expecting `oneof:` to accept a plain list of type names the way `types:` does; refactoring named methods into variants without converting them to lambdas.","solutions":["Express each variant as a lambda/proc: `oneof: [-> { requires :name, type: String }, -> { requires :id, type: Integer }]`","Wrap named methods as `-> { by_name_variant }` so the variant is still a Proc","For plain type unions (not structural alternatives), use `types: [Integer, String]`, not `oneof:`"],"exampleFix":"# before\nrequires :filter, type: Hash, oneof: [:by_name, :by_id]\n\n# after\nrequires :filter, type: Hash, oneof: [\n  -> { requires :name, type: String },\n  -> { requires :id, type: Integer }\n]","handlingStrategy":"type-guard","validationCode":"def oneof_variants_valid?(opts)\n  variants = opts[:oneof]\n  opts[:type] == Hash && variants.is_a?(Array) && !variants.empty? && variants.all?(Proc)\nend","typeGuard":"def oneof_shape?(opts)\n  opts[:type] == Hash && opts[:oneof].is_a?(Array) && opts[:oneof].any? && opts[:oneof].all?(Proc)\nend\n\nreturn unless oneof_shape?(filter_opts)","tryCatchPattern":"begin\n  requires :filter, type: Hash, oneof: variant_list\nrescue ArgumentError => e\n  raise \"oneof variants must be procs (got #{variant_list.map(&:class).inspect}): #{e.message}\"\nend","preventionTips":["Build variant lists as lambdas from the start: `VARIANTS = [-> { ... }, -> { ... }].freeze`","Never mix symbols or hashes into oneof lists — wrap named methods as `-> { method_name }`","Use `types:` for plain type unions so proc shapes never come into question"],"tags":["grape","params","oneof","validation","argumenterror","dsl"],"backgroundTag":"oneof-validation-misconfigured","analyzedSha":"22d7975629846a3c0c7bd2b34e140a7a1b4af8f6","analyzedAt":"2026-08-21T17:03:54.627Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}