The Perils of “rescue Exception”
March 3rd, 2012 · 9 Comments
Ruby’s standard exception handling looks like this.
begin do_something rescue => ex # do something with the error end
This will catch all StandardErrors and its subclasses. A frequent issue I see in Ruby code is this:
begin do_something rescue Exception => ex # Apparently I REALLY want to make sure this block # won't interfere will the outside code. end
This is a BAD, BAD idea, dear reader, and here’s why. Ruby uses Exceptions for other things than your application code errors. One example is the Interrupt class which is a SignalException. Ruby sends this Exception to all threads so that when the process gets an INT/Ctrl-C signal, all the threads will unwind and the process will shutdown. If you rescue Exception, you will potentially catch this exception and ignore it, making your thread and process an unkillable computing zombie. Your only choice will be to pull out your kill -9 shotgun and aim for the head.
Here’s an example of a Ruby script you cannot shutdown gracefully. Run it and you’ll see exactly the behavior I’ve described.
while true begin sleep 5 puts 'ping' rescue Exception => ex puts "Mmmmm, brains" end end
So remember, your application errors should be subclasses of StandardError and if you want to catch everything, just stick will plain old “rescue => ex”. Your application will behave better for it.
Tags: Ruby
9 responses so far ↓
1 Avdi Grimm // Mar 3, 2012 at 12:30 pm
Indeed! Another thing I see is “rescue Object”, which is especially silly since Ruby will not allow you to raise an object not derived from Exception.
For anyone interested in using Ruby exceptions more effectively, may I humbly suggest “Exceptional Ruby”?
2 Bradly Feeley // Mar 3, 2012 at 2:48 pm
Mike,
Does the ‘=>’ have a special meaning in ‘rescue => ex’. Obviously you usually see the hash rocket in hashes, so does it have another function that isn’t used very often, or is this just a special use just when rescuing.
3 Mike Perham // Mar 3, 2012 at 6:51 pm
Humbly seconded, Avdi’s book is illuminating. In fact I was just suggesting it to TheClymb dev team yesterday!
4 Eric Hankins // Mar 6, 2012 at 1:28 pm
Amen, and amen. I discovered this myself after rescuing Exception in all of my daemons and then wondering why I couldn’t kill them. This is great advice.
5 Steven Harms // Mar 10, 2012 at 11:48 am
Here’s my theory. This mis-idiom issue is an error caused by over-generalization from a specific case.
Consider:
With output:
I think the thought process is:
"Oh, I handle a ZeroDivsionError or a RuntimeError by putting its name before the =>. I will handle a more general exception by putting 'Exception' in front of the =>."
6 Conrad Irwin // Apr 26, 2012 at 12:51 pm
In some circumstances you do want to catch *all* the exceptions, for example the REPL loop in Pry shouldn’t die if it gets a TimeoutError or a LoadError.
Our solution, made with ideas from Avdi’s Exceptional Ruby, was to define a module that matches all exceptions except for the very few we don’t want to catch:
“`
# As a REPL, we often want to catch any unexpected exceptions that may have
# been raised; however we don’t want to go overboard and prevent the user
# from exiting Pry when they want to.
module RescuableException
def self.===(exception)
case exception
# Catch when the user hits ^C (Interrupt < SignalException), and assume
# that they just wanted to stop the in-progress command (just like bash etc.)
when Interrupt
true
# Don't catch signals (particularly not SIGTERM) as these are unlikely to be
# intended for pry itself. We should also make sure that Kernel#exit works.
when SystemExit, SignalException
false
# All other exceptions will be caught.
else
true
end
end
end
“`
We then use "rescue RescuableException" in place of "rescue Exception".
7 Judson Lester // May 1, 2012 at 10:53 pm
The only counterargument I’d make is that (sadly) some gems use their own exception classes that don’t descend from StandardError, which makes rescue => ex inadequate.
8 Code - sane_timeout: A Replacement for Ruby’s Standard Library Timeout // Sep 15, 2012 at 9:14 pm
[...] should typically only rescue StandardError, or specific other errors appropriate to the context. Mike Perhaps covers the issue here. But the reality is that some code, for reasons bad, questionable, or good, will sometimes rescue [...]
9 Andreas Mohr // Oct 1, 2012 at 1:40 pm
Care to listen to a counter argument?
(warning: Ruby n00b here)
If I’m not mistaken it should be noted that this does not apply to all (perhaps not even most?) cases of exception handling.
While the
begin
rescue => ex
end
block design would be useful for cases of *finalized* exception handling (i.e., final handling of an error situation in local context), there are other cases where you would want to have local cleanup *and* have the exception bubble up (i.e., re-throw), to signal a murky situation to upper layers as well.
In such cases IMHO
begin
rescue Exception => e
raise
end
is exactly the right thing to do to ensure that *all* exceptions, not only application-level, are intercepted properly and then passed on.
Leave a Comment