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

  1. Delete the duplicate registration and keep exactly one handler per exception class per scope
  2. If different endpoints need different behavior for the same exception, split them into separate namespaces rather than re-registering in the same scope
  3. 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

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


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