ruby-grape/grape · warning
Grape: rescue_from #{klass} was already registered in this s
Error message
Grape: rescue_from #{klass} was already registered in this scope; the first handler is kept and this one will never run. What it means
Grape matches `rescue_from` handlers in registration order and the first match wins. Registering the same exception class twice in one scope with a different handler means the second registration can never run; the shadowed-rescue audit detects it via class identity (`klass <= already`) plus handler inequality (`!existing.equal?(handler)`) and warns at definition time. A sibling message covers the ancestor case, where a broader class registered earlier shadows a later, narrower one.
Source
Thrown at lib/grape/util/shadowed_rescue_handlers.rb:42
# Only a scope's own registrations are compared: across scopes the nearest
# one deliberately wins, so an inner +rescue_from StandardError+ shadowing
# 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
- Delete the duplicate registration and keep exactly one handler per exception class per scope
- If different endpoints need different behavior for the same exception, split them into separate namespaces rather than re-registering in the same scope
- Register the most specific class before broader ones so the ancestor-shadowing audit stays quiet too
Example fix
# before
rescue_from ActiveRecord::RecordNotFound do |e| error!('missing', 404) end
# ... later in the same scope ...
rescue_from ActiveRecord::RecordNotFound do |e| error!('gone', 410) end # never runs
# after — one registration per scope
rescue_from ActiveRecord::RecordNotFound do |e| error!('missing', 404) end Defensive patterns
Strategy: validation
Validate before calling
def assert_no_duplicate_rescue_from!(registrations)
seen = {}
registrations.each do |klass, handler|
warn "rescue_from #{klass} registered twice" if seen[klass] && !seen[klass].equal?(handler)
seen[klass] ||= handler
end
end Prevention
- Keep one rescue_from per exception class per scope; audit concerns that register handlers
- Register the most specific exception classes first, broad ones last
- After including shared modules, grep the scope for repeated `rescue_from SomeClass` lines
When it happens
Trigger: `rescue_from ActiveRecord::RecordNotFound` declared twice in the same API class or namespace with different handlers — e.g. after copy-paste, or after including two shared concerns that each register it; dev-reload flows that re-run registration in the same scope.
Common situations: Shared concern/module registering common handlers included alongside a local registration of the same class; refactors that duplicate a rescue_from block; upgrading Grape to a version that added this audit to previously silent duplicates.
Related errors
- rescue_from #{meta_selector.inspect} does not accept additio
- both :with option and block cannot be passed
- with: #{with.class}, expected Symbol, String or Proc
- Either contract or block must be provided
- must supply type for coerce_with
AI-assisted analysis of ruby-grape/grape@22d7975629 (2026-08-21).
Data as JSON: /api/errors/7defc89a25769a43.
Report an issue: GitHub.