"Never rescue Exception in Ruby!"
Maybe you've heard this before. It's good advice, but it's pretty confusing unless you're already in the know. Let's break this statement down and see what it means.
You probably know that in Ruby, you can rescue exceptions like so:
begin
do_something()
rescue => e
puts e # e is an exception object containing info about the error.
end
And you can rescue specific errors by providing the classname of the error.
begin
do_something()
rescue ActiveRecord::RecordNotFound => e
puts e # Only rescues RecordNotFound exceptions, or classes that inherit from RecordNotFound
end
Every type of exception in Ruby is just a class. In the example above, ActiveRecord::RecordNotFound is just the name of a class that follows certain conventions.
This is important because when you rescue RecordNotFound
, you also rescue any exceptions that inherit from it.
Why you shouldn't rescue Exception
The problem with rescuing Exception
is that it actually rescues every exception that inherits from Exception
. Which is....all of them!
That's a problem because there are some exceptions that are used internally by Ruby. They don't have anything to do with your app, and swallowing them will cause bad things to happen.
Here are a few of the big ones:
SignalException::Interrupt - If you rescue this, you can't exit your app by hitting control-c.
ScriptError::SyntaxError - Swallowing syntax errors means that things like
puts("Forgot something)
will fail silently.NoMemoryError - Wanna know what happens when your program keeps running after it uses up all the RAM? Me neither.
begin
do_something()
rescue Exception => e
# Don't do this. This will swallow every single exception. Nothing gets past it.
end
I'm guessing that you don't really want to swallow any of these system-level exceptions. You only want to catch all of your application level errors. The exceptions caused YOUR code.
Luckily, there's an easy way to to this.
Rescue StandardError Instead
All of the exceptions that you should care about inherit from StandardError
. These are our old friends:
NoMethodError - raised when you try to invoke a method that doesn't exist
TypeError - caused by things like
1 + ""
RuntimeError - who could forget good old RuntimeError?
To rescue errors like these, you'll want to rescue StandardError
. You COULD do it by writing something like this:
begin
do_something()
rescue StandardError => e
# Only your app's exceptions are swallowed. Things like SyntaxErrror are left alone.
end
But Ruby has made it much easier for use.
When you don't specify an exception class at all, ruby assumes you mean StandardError. So the code below is identical to the above code:
begin
do_something()
rescue => e
# This is the same as rescuing StandardError
end
Custom Exceptions Should Inherit from StandardError
So what does this mean for you if you're creating your own custom exceptions?
It means you should always inherit from StandardError
, and NEVER from Exception
. Inheriting from Exception is bad because it breaks the expected behavior of rescue. People will think they're rescuing all application-level errors but yours will just sail on through.
class SomethingBad < StandardError
end
raise SomethingBad
The Exception Tree
Since Ruby's exceptions are implemented in a class heirarchy, it can be helpful to see it laid out. Below is a list of exception classes that ship with Ruby's standard library. Third-party gems like rails will add additional exception classes to this chart, but they will all inherit from some class on this list.
Exception
NoMemoryError
ScriptError
LoadError
NotImplementedError
SyntaxError
SignalException
Interrupt
StandardError
ArgumentError
IOError
EOFError
IndexError
LocalJumpError
NameError
NoMethodError
RangeError
FloatDomainError
RegexpError
RuntimeError
SecurityError
SystemCallError
SystemStackError
ThreadError
TypeError
ZeroDivisionError
SystemExit
fatal