ruby-grape/grape · warning

Passing a positional options Hash to `#{method_name}` is dep

Error message

Passing a positional options Hash to `#{method_name}` is deprecated. Pass keyword arguments instead.

What it means

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.

Source

Thrown at lib/grape/dsl/parameters.rb:231

        params = map_params(params, @element) if @element
        params
      end

      private

      # @deprecated A trailing positional options Hash is deprecated; pass keyword
      #   arguments instead. Before Ruby 3 keyword separation this Hash was pulled
      #   off the argument list by `extract_options!`; now it lands in the splat and
      #   would silently be treated as a parameter name.
      def legacy_options?(args)
        args.size > 1 && args.last.is_a?(Hash)
      end

      # Re-invokes +method_name+ with the trailing Hash splatted as keyword
      # arguments, so Ruby routes its keys to the same place they would have
      # reached had the caller omitted the braces.
      def redispatch_legacy_options(method_name, args, opts, &)
        Grape.deprecator.warn("Passing a positional options Hash to `#{method_name}` is deprecated. Pass keyword arguments instead.")
        __send__(method_name, *args[0..-2], **args.last.merge(opts), &)
      end

      def first_hash_key_or_param(parameter)
        parameter.is_a?(Hash) ? parameter.keys.first : parameter
      end

      def map_params(params, element, is_array: false)
        case params
        when Array
          params.map do |el|
            map_params(el, element, is_array: true)
          end
        when Hash
          params[element] || (@optional && is_array ? EmptyOptionalValue : {})
        when EmptyOptionalValue
          EmptyOptionalValue
        else

View on GitHub (pinned to 22d7975629)

Solutions

  1. Pass keywords without braces: `requires :id, type: Integer, desc: 'user id'`
  2. In macros, double-splat the options: `requires name, **opts`
  3. Run CI with `Grape.deprecator.behavior = :raise` to catch remaining positional-hash call sites

Example fix

# before
requires :id, { type: Integer, desc: 'user id' }

# after
requires :id, type: Integer, desc: 'user id'
Defensive patterns

Strategy: validation

Validate before calling

# CI guard: deprecations raise in the test env, catching positional option hashes
Grape.deprecator.behavior = :raise if Rails.env.test?

Prevention

When it happens

Trigger: `requires :id, { type: Integer, desc: 'user id' }`; `optional :page, { default: 1 }`; any parameter DSL call whose last positional argument is a Hash.

Common situations: Pre-Ruby-3 codebases; ERB templates or generators emitting hash literals into DSL calls; shared macros that splat an options hash positionally into `requires`.

Related errors


AI-assisted analysis of ruby-grape/grape@22d7975629 (2026-08-21). Data as JSON: /api/errors/41261a541ddef04a. Report an issue: GitHub.