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!

brian.hempel@collectiveidea.com

Comments

  1. August 05, 2014 at 18:43 PM

    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.