Repository navigation
Rank a nested rescue_from :all ahead of the enclosing scope's handlers - #3073
Merged
Merged
Conversation
ericproulx
force-pushed
the
fix/nested-rescue-all-precedence
branch
from
November 10, 2026 09:04
bcf2f67 to
a3a9633
Compare
dblock
approved these changes
Nov 10, 2026
A rescue_from :all declared in a namespace or a mounted API lost to any class handler of an enclosing scope that matched, although a nested class handler wins over an outer one whatever the classes are (#1405). The class handlers of every scope were scanned before the catch-all was consulted. The scope that declared rescue_from :all now ends its handlers with an :all entry, so the catch-all ranks behind its own scope's class handlers and ahead of every enclosing scope's. An exception it does not catch, one that is not a StandardError, scans past it as before, and so does one raised inside a rescue_from block, which is never handed to a catch-all. The mount-order specs from #2384 pinned the outer class handler winning; they now expect the mounted API's catch-all, for either mount order. Supersedes #1982. See #1975. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ericproulx
force-pushed
the
fix/nested-rescue-all-precedence
branch
from
November 10, 2026 10:52
a3a9633 to
9045960
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A
rescue_from :alldeclared in a namespace or a mounted API lost to any class handler of an enclosing scope that matched:A nested class handler already wins over an outer one whatever the classes are (#1405, README "Rescuing exceptions inside namespaces"). So an inner
rescue_from StandardErrorbeat the outerZeroDivisionError, while an innerrescue_from :all, the same catch-all written differently, lost to it. The cause was ordering:Middleware::Errorscanned the class handlers of every scope before it consulted:all, which is a single inherited value with no position in the chain.Supersedes #1982. See #1975.
Change
InheritableSetting#rescue_handlersends the handlers of a scope that declaredrescue_from :allwith an:allentry. The entry is added on read, so it always follows that scope's own class handlers, however the declarations were interleaved. The catch-all therefore ranks behind its own scope's class handlers and ahead of every enclosing scope's.Middleware::Errorstops the scan at that entry for an exception:allcatches (aStandardError) and goes on to the catch-all as before, after the Grape exception checks.:alldoes not catch, such asNotImplementedError, scans past the entry and still reaches an enclosingrescue_from Exception.rescue_fromblock is never handed to a catch-all, so redispatch ignores the entry and an enclosing scope's handler still takes it.rescue_subclasses: false) are untouched. They are scanned before every subclass handler, whatever scope registered them, which predates this change and applies to nested class handlers too.Behaviour change
The mount-order specs from #2384 (
spec/grape/api/mount_and_helpers_order_spec.rb) pinned the outer class handler winning over a mounted API's:all. They now expect the mounted API's catch-all, and still give the same answer for either mount order, which is what they were written to check. UPGRADING describes the change and how to keep the old answer: register the class in the nested scope too.Specs
spec/grape/api_spec.rb, next to the existing "rescues defined by mounted" precedence specs::allbeats an outer class handler (fails on master);:all;:alldoes not catch reaches the outerrescue_from Exception, while aStandardErrorstops at the nested:all;:allblock is redispatched to the outer class handler.Each of the last three guard specs was mutation-checked: putting the
:allentry ahead of the scope's own handlers, letting redispatch stop at it, or letting it stop non-StandardErrors each fails exactly the matching spec.🤖 Generated with Claude Code