ruby-grape/grape · warning

Returning or throwing a Hash from a rescue handler is deprec

Error message

Returning or throwing a Hash from a rescue handler is deprecated. Use `error!(...)` or a `Grape::Exceptions::ErrorResponse` instead.

What it means

A `rescue_from` handler used to be allowed to return (or throw) a Hash of `{ message:, status:, headers: }`, which Grape converted into a rack response. That contract is deprecated: `error?` detects exactly that three-key Hash, warns via `Grape.deprecator`, and directs you to `error!` or a `Grape::Exceptions::ErrorResponse`. Note that a Hash missing any of the three keys is not treated as an error response at all — it becomes the response body verbatim.

Source

Thrown at lib/grape/middleware/error.rb:220

      end

      def error!(message, status = default_status, headers = {}, backtrace = [], original_exception = nil)
        env[Grape::Env::API_ENDPOINT].status(status) # not error! inside route
        merged_headers = { Rack::CONTENT_TYPE => content_type }.merge!(headers)
        error = Grape::Exceptions::ErrorResponse.new(
          status:, message:, headers: merged_headers, backtrace:, original_exception:
        )
        rack_response(status, merged_headers, format_message(error))
      end

      def error?(response)
        case response
        when Grape::Exceptions::ErrorResponse
          true
        when Hash
          return false unless response.key?(:message) && response.key?(:status) && response.key?(:headers)

          Grape.deprecator.warn(
            'Returning or throwing a Hash from a rescue handler is deprecated. ' \
            'Use `error!(...)` or a `Grape::Exceptions::ErrorResponse` instead.'
          )
          true
        else
          false
        end
      end
    end
  end
end

View on GitHub (pinned to 22d7975629)

Solutions

  1. Call `error!('missing', 404)` inside the handler and let Grape build the response
  2. Or construct the structured object: `throw :error, Grape::Exceptions::ErrorResponse.new(message: 'missing', status: 404, headers: {})`
  3. Audit handlers during upgrade with `Grape.deprecator.behavior = :raise`

Example fix

# before
rescue_from NotFoundError do |e|
  { message: 'not found', status: 404, headers: {} }
end

# after
rescue_from NotFoundError do |e|
  error!('not found', 404)
end
Defensive patterns

Strategy: validation

Validate before calling

# CI guard: hash-returning rescue handlers raise in the test env
Grape.deprecator.behavior = :raise if Rails.env.test?

Try / catch

# In request specs, assert on the rendered error instead of the handler's return value:
# expect { get '/missing' }.to change { response.status }.to(404)
# — this fails loudly if a handler still returns a Hash

Prevention

When it happens

Trigger: `rescue_from NotFoundError do |e| { message: 'missing', status: 404, headers: {} } end`; throwing the Hash: `throw :error, { message: ..., status: ..., headers: ... }` from a handler or custom middleware.

Common situations: Apps written against very old Grape; upgrading across this deprecation; handlers shared with Sinatra-style code that return hashes.

Related errors


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