{"record":{"id":"7defc89a25769a43","repo":"ruby-grape/grape","slug":"grape-rescue-from-klass-was-already-registered","errorCode":null,"errorMessage":"Grape: rescue_from #{klass} was already registered in this scope; the first handler is kept and this one will never run.","messagePattern":"Grape: rescue_from #(.+?) was already registered in this scope; the first handler is kept and this one will never run\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"lib/grape/util/shadowed_rescue_handlers.rb","lineNumber":42,"sourceCode":"      # Only a scope's own registrations are compared: across scopes the nearest\n      # one deliberately wins, so an inner +rescue_from StandardError+ shadowing\n      # an outer +rescue_from ArgumentError+ is the documented behaviour rather\n      # than a mistake. Classes sharing a handler object are skipped too, since\n      # +rescue_from A, B+ registers one handler for both and the entry that\n      # loses to the other changes nothing.\n      def warn_about(registered, mapping)\n        return if registered.empty?\n\n        mapping.each do |klass, handler|\n          covered_by, = registered.find { |already, existing| klass <= already && !existing.equal?(handler) }\n          next unless covered_by\n\n          warn(message_for(klass, covered_by))\n        end\n      end\n\n      def message_for(klass, covered_by)\n        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\n\n        \"Grape: rescue_from #{klass} will never run — #{covered_by} was registered earlier in the same scope \" \\\n          'and is matched first. Register the more specific class before the broader one.'\n      end\n    end\n  end\nend\n","sourceCodeStart":24,"sourceCodeEnd":50,"githubUrl":"https://github.com/ruby-grape/grape/blob/22d7975629846a3c0c7bd2b34e140a7a1b4af8f6/lib/grape/util/shadowed_rescue_handlers.rb#L24-L50","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","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"],"exampleFix":"# before\nrescue_from ActiveRecord::RecordNotFound do |e| error!('missing', 404) end\n# ... later in the same scope ...\nrescue_from ActiveRecord::RecordNotFound do |e| error!('gone', 410) end # never runs\n\n# after — one registration per scope\nrescue_from ActiveRecord::RecordNotFound do |e| error!('missing', 404) end","handlingStrategy":"validation","validationCode":"def assert_no_duplicate_rescue_from!(registrations)\n  seen = {}\n  registrations.each do |klass, handler|\n    warn \"rescue_from #{klass} registered twice\" if seen[klass] && !seen[klass].equal?(handler)\n\n    seen[klass] ||= handler\n  end\nend","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["grape","rescue-from","exception-handling","warning","dsl"],"backgroundTag":"duplicate-exception-handler","analyzedSha":"22d7975629846a3c0c7bd2b34e140a7a1b4af8f6","analyzedAt":"2026-08-21T17:03:54.627Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}