ruby-grape/grape · warning
Grape: rescue_from #{klass} will never run — #{covered_by} w
Error message
Grape: rescue_from #{klass} will never run — #{covered_by} was registered earlier in the same scope and is matched first. Register the more specific class before the broader one. What it means
Grape emits this warning (via Kernel#warn to $stderr, not a raised exception) when a rescue_from registration in a scope can never execute because a broader exception class registered EARLIER in the same scope is matched first. Grape's error middleware resolves handlers with find over the ordered handler map, so the first registered class that covers the raised exception (klass <= already, i.e. the raised class is a subclass of the registered one) wins — making the later, more specific handler dead code. The fix the message prescribes is ordering: register the most specific class before the broader one. It only compares a scope's own registrations: an inner scope's rescue_from legitimately overrides an outer one, and rescue_from A, B (one shared handler object) is skipped.
Source
Thrown at lib/grape/util/shadowed_rescue_handlers.rb:44
# an outer +rescue_from ArgumentError+ is the documented behaviour rather
# than a mistake. Classes sharing a handler object are skipped too, since
# +rescue_from A, B+ registers one handler for both and the entry that
# loses to the other changes nothing.
def warn_about(registered, mapping)
return if registered.empty?
mapping.each do |klass, handler|
covered_by, = registered.find { |already, existing| klass <= already && !existing.equal?(handler) }
next unless covered_by
warn(message_for(klass, covered_by))
end
end
def message_for(klass, covered_by)
return "Grape: rescue_from #{klass} was already registered in this scope; the first handler is kept and this one will never run." if klass == covered_by
"Grape: rescue_from #{klass} will never run — #{covered_by} was registered earlier in the same scope " \
'and is matched first. Register the more specific class before the broader one.'
end
end
end
end
View on GitHub (pinned to 22d7975629)
Solutions
- Reorder the rescue_from calls in that scope so more specific exception classes are registered before broader ones (e.g. ArgumentError before StandardError), matching how you would order native Ruby rescue clauses.
- If the broad handler is meant as a catch-all, replace `rescue_from StandardError` with the meta selector `rescue_from :all` — it is consulted only after all class-keyed handlers, so specific handlers keep winning regardless of order.
- Move the broad handler to an outer scope (base API class) and keep specific handlers in the inner namespace/endpoint — cross-scope shadowing is documented, intended behavior (nearest scope wins) and is not warned.
- Delete the shadowed registration if it is redundant dead code and no reorder is intended.
Example fix
# before
class MyAPI < Grape::API
rescue_from StandardError { |_e| error!('server error', 500) }
rescue_from ArgumentError { |_e| error!('bad input', 400) } # never runs: StandardError matched first
end
# after (option 1: specific first)
class MyAPI < Grape::API
rescue_from ArgumentError { |_e| error!('bad input', 400) }
rescue_from StandardError { |_e| error!('server error', 500) }
end
# after (option 2: keep order, make the catch-all a meta selector)
class MyAPI < Grape::API
rescue_from ArgumentError { |_e| error!('bad input', 400) }
rescue_from :all with: ->(_e) { error!('server error', 500) } # consulted after class-keyed handlers
end Defensive patterns
Strategy: validation
Validate before calling
# Predicate mirroring Grape's check (klass <= already means the earlier broader class covers klass):
# Returns the already-registered broader class that would shadow klass, or nil.
def covering_class(registered_classes, klass)
registered_classes.find { |broader| klass <= broader } # klass <= broader <=> klass.ancestors.include?(broader)
end
# Use it when wiring handlers programmatically so a shadow never gets registered:
handlers = [[ArgumentError, ->(e) { error!(e.message, 400) }],
[StandardError, ->(e) { error!('server error', 500) }]]
registered = []
handlers.each do |klass, handler|
broader = covering_class(registered, klass)
raise ArgumentError, "#{klass} would be shadowed by earlier #{broader}" if broader
registered << klass
# rescue_from klass, with: handler ...
end Type guard
# Narrowing predicate for Ruby: true if registering klass in this scope is safe
# (i.e. no earlier broader class already covers it).
def rescueable_here?(registered_classes, klass)
registered_classes.none? { |broader| klass <= broader }
end
rescueable_here?([StandardError], ArgumentError) # => false — would be shadowed, skip or reorder
rescueable_here?([ArgumentError], StandardError) # => true — broader after specific is fine Try / catch
# It is a Kernel#warn to $stderr, not a raised exception — 'catch' it by asserting on stderr
# in the spec that loads the API class, so a shadowed handler fails CI instead of shipping:
RSpec.describe MyAPI do
it 'registers no shadowed rescue_from handlers' do
expect { load 'app/api/my_api.rb' }
.not_to output(/Grape: rescue_from .* will never run/).to_stderr
end
end Prevention
- Treat rescue_from order like native Ruby rescue order: most specific class first, broadest last, within each scope.
- Use rescue_from :all for catch-alls instead of rescue_from StandardError — class-keyed handlers always beat the meta selector, so order stops mattering.
- Keep broad handlers on the base/outer API class and specific handlers in namespaces/endpoints; inner scopes intentionally override outer ones.
- Add a spec asserting the API class loads without the 'Grape: rescue_from ... will never run' warning on $stderr.
- Remember re-registering the same class in a scope keeps the FIRST handler (and its position) — don't rely on a later registration to override; scope it more tightly instead.
When it happens
Trigger: Calling rescue_from with exception classes in a Grape::API class or namespace such that a later call names a class that is a descendant of a class already registered in the SAME scope — e.g. `rescue_from StandardError` followed by `rescue_from ArgumentError`, or `rescue_from Grape::Exceptions::Base` followed by `rescue_from Grape::Exceptions::ValidationErrors`. Only fires for class-keyed registrations made with the default rescue_subclasses: true (the subclasses: true map passed to add_rescue_handlers); the meta selectors rescue_from :all / :grape_exceptions / :internal_grape_exceptions never trigger it, and re-registering the exact same class with the same handler object is also skipped. Fires at class-body load time, the moment rescue_from is called.
Common situations: Copying an endpoint's error handling into an API class that already declares a catch-all rescue_from StandardError near the top. Refactors or gem upgrades that insert a broad handler above existing specific ones. Assuming Ruby's native rescue semantics (most specific clause wins regardless of order) instead of Grape's first-match-wins registration order. Moving a broad handler from a base class into the same scope where specific handlers already live. Previously (older Grape) this was silent dead code — a version adding this diagnostic surfaces latent ordering bugs.
Related errors
- rescue_from #{meta_selector.inspect} does not accept additio
- Returning or throwing a Hash from a rescue handler is deprec
- both :with option and block cannot be passed
- with: #{with.class}, expected Symbol, String or Proc
- Grape: rescue_from #{klass} was already registered in this s
AI-assisted analysis of ruby-grape/grape@22d7975629 (2026-08-21).
Data as JSON: /api/errors/1c32c9033ff7e93d.
Report an issue: GitHub.