Why Isn't Ruby Rescuing My Exception?
I was really confused yesterday when rescue
refused to catch my exception.
begin
user.update # raises a MyApp::Error
rescue => error
# but was not rescued
end
Isn’t rescue
supposed to rescue everything? Actually, no. Without specifying which exception classes to catch, rescue
will only catch exceptions that inherit from StandardError
.
That means…
begin
rescue
end
…is really just short-hand for:
begin
rescue StandardError
end
Some folks recommend explicitly writing rescue StandardError
whenever you want a plain rescue
. I don’t have a strong opinion, because once you know why Ruby behaves the way it does, you won’t be caught off guard.
Why does Ruby default to only catching exceptions that inherit from StandardError
? Let’s look at some examples from the Exception
class hierarchy.
Events that should stop your program (like the user hitting ctrl-c
in the terminal or require
failing to find a file) inherit directly from Exception
. Problems that might be recoverable (like calling fall\_in\_love
on nil
or passing "chocolate"
to Time.parse
) inherit from StandardError
. This means that our code should almost always inherit from StandardError
.
Sure enough, that was my bug. I changed…
class MyApp::Error < Exception
end
…to…
class MyApp::Error < StandardError
end
…and the error was properly caught!
Comments
Just a tip; you can define your own exceptions like this:
MyApp::Error = Class.new(StandardError)
One benefit being no ugly trailing ‘end’. Also, and I know the point was to illustrate the idea, but try to be more specific: Error could be anything, WidgetFailureError is much more descriptive. Then:
module MyApp
WidgetFailureError = Class.new(StandardError)
class Widget
…
…
Nice find though.