{"record":{"id":"936a1e8a97c41e06","repo":"ruby-grape/grape","slug":"passing-a-positional-options-hash-to-desc-is-dep","errorCode":null,"errorMessage":"Passing a positional options Hash to `desc` is deprecated. Pass keyword arguments instead.","messagePattern":"Passing a positional options Hash to `desc` is deprecated\\. Pass keyword arguments instead\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"lib/grape/dsl/desc.rb","lineNumber":55,"sourceCode":"      # @example\n      #\n      #     desc 'create a user'\n      #     post '/users' do\n      #       # ...\n      #     end\n      #\n      #     desc 'find a user' do\n      #       detail 'locates the user from the given user ID'\n      #       failure [ [404, 'Couldn\\'t find the given user' ] ]\n      #       success User::Entity\n      #     end\n      #     get '/user/:id' do\n      #       # ...\n      #     end\n      #\n      def desc(description, *legacy_options, **options, &config_block)\n        if legacy_options.any?\n          Grape.deprecator.warn('Passing a positional options Hash to `desc` is deprecated. Pass keyword arguments instead.')\n          options = legacy_options.first.merge(options)\n        end\n\n        settings =\n          if config_block\n            endpoint_config = defined?(configuration) ? configuration : nil\n            Grape::Util::ApiDescription.new(description, endpoint_config, &config_block).settings\n          else\n            options.merge(description:)\n          end\n        # Only the route scope is consumed downstream (by +route+ and the\n        # route's readers, e.g. +http_codes+); the namespace scope was\n        # write-only, so it is no longer populated.\n        inheritable_setting.route_description = settings\n      end\n    end\n  end\nend","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/ruby-grape/grape/blob/22d7975629846a3c0c7bd2b34e140a7a1b4af8f6/lib/grape/dsl/desc.rb#L37-L73","documentation":"`desc` now takes keyword arguments. Under Ruby 3 keyword separation, a Hash passed positionally (`desc 'text', { success: Entity }`) no longer lands in the options slot — Grape detects it in the splat, warns through `Grape.deprecator`, and merges it with the keyword options for backwards compatibility. The positional form will be removed in a future release.","triggerScenarios":"`desc 'find a user', { summary: 'x', success: User::Entity }` (explicit braces); `desc('find a user', { failure: [[404, 'Not Found']] })`; any `desc` call whose second positional argument is a Hash.","commonSituations":"Apps written before the Ruby 3 keyword split; upgrading Grape across a major version that keyword-ified the DSL; code generators that emit hash literals into `desc` calls.","solutions":["Drop the braces and pass keywords: `desc 'find a user', summary: 'x', success: User::Entity`","Or use the block form: `desc 'find a user' do ... end`","Set `Grape.deprecator.behavior = :raise` in the test environment to flush out every remaining call site"],"exampleFix":"# before\ndesc 'find a user', { success: User::Entity, failure: [[404, 'Not Found']] }\n\n# after\ndesc 'find a user', success: User::Entity, failure: [[404, 'Not Found']]","handlingStrategy":"validation","validationCode":"# CI guard: turn deprecations into failures so positional-hash `desc` calls cannot slip through\nGrape.deprecator.behavior = :raise if ENV['CI']","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Write `desc 'text', key: value` without braces — never `desc('text', { ... })`","Run the suite with `Grape.deprecator.behavior = :raise` during upgrades","Search the codebase for `desc(` with a brace literal as part of major-version upgrades"],"tags":["grape","desc","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"}