{"record":{"id":"41261a541ddef04a","repo":"ruby-grape/grape","slug":"passing-a-positional-options-hash-to-method-nam","errorCode":null,"errorMessage":"Passing a positional options Hash to `#{method_name}` is deprecated. Pass keyword arguments instead.","messagePattern":"Passing a positional options Hash to `#(.+?)` is deprecated\\. Pass keyword arguments instead\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"lib/grape/dsl/parameters.rb","lineNumber":231,"sourceCode":"        params = map_params(params, @element) if @element\n        params\n      end\n\n      private\n\n      # @deprecated A trailing positional options Hash is deprecated; pass keyword\n      #   arguments instead. Before Ruby 3 keyword separation this Hash was pulled\n      #   off the argument list by `extract_options!`; now it lands in the splat and\n      #   would silently be treated as a parameter name.\n      def legacy_options?(args)\n        args.size > 1 && args.last.is_a?(Hash)\n      end\n\n      # Re-invokes +method_name+ with the trailing Hash splatted as keyword\n      # arguments, so Ruby routes its keys to the same place they would have\n      # reached had the caller omitted the braces.\n      def redispatch_legacy_options(method_name, args, opts, &)\n        Grape.deprecator.warn(\"Passing a positional options Hash to `#{method_name}` is deprecated. Pass keyword arguments instead.\")\n        __send__(method_name, *args[0..-2], **args.last.merge(opts), &)\n      end\n\n      def first_hash_key_or_param(parameter)\n        parameter.is_a?(Hash) ? parameter.keys.first : parameter\n      end\n\n      def map_params(params, element, is_array: false)\n        case params\n        when Array\n          params.map do |el|\n            map_params(el, element, is_array: true)\n          end\n        when Hash\n          params[element] || (@optional && is_array ? EmptyOptionalValue : {})\n        when EmptyOptionalValue\n          EmptyOptionalValue\n        else","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/ruby-grape/grape/blob/22d7975629846a3c0c7bd2b34e140a7a1b4af8f6/lib/grape/dsl/parameters.rb#L213-L249","documentation":"The parameter DSL (`requires`, `optional`, etc.) now takes keyword options. A trailing positional Hash (`requires :id, { type: Integer }`) used to be peeled off by `extract_options!`; under Ruby 3 it lands in the splat and would silently be treated as another parameter name. Grape detects the shape, warns via `Grape.deprecator`, and re-invokes the method with the Hash converted to keywords.","triggerScenarios":"`requires :id, { type: Integer, desc: 'user id' }`; `optional :page, { default: 1 }`; any parameter DSL call whose last positional argument is a Hash.","commonSituations":"Pre-Ruby-3 codebases; ERB templates or generators emitting hash literals into DSL calls; shared macros that splat an options hash positionally into `requires`.","solutions":["Pass keywords without braces: `requires :id, type: Integer, desc: 'user id'`","In macros, double-splat the options: `requires name, **opts`","Run CI with `Grape.deprecator.behavior = :raise` to catch remaining positional-hash call sites"],"exampleFix":"# before\nrequires :id, { type: Integer, desc: 'user id' }\n\n# after\nrequires :id, type: Integer, desc: 'user id'","handlingStrategy":"validation","validationCode":"# CI guard: deprecations raise in the test env, catching positional option hashes\nGrape.deprecator.behavior = :raise if Rails.env.test?","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass parameter options as keywords: `requires :id, type: Integer`","In macros, double-splat: `requires name, **opts`","During upgrades, grep for `requires :x, {` and `optional :x, {` patterns"],"tags":["grape","params","deprecation","ruby3-keywords","dsl"],"backgroundTag":"deprecated-api-usage","analyzedSha":"22d7975629846a3c0c7bd2b34e140a7a1b4af8f6","analyzedAt":"2026-08-21T17:03:54.627Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}