Ruby's Exception vs StandardError: What's the difference?
"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