We're working on something new! Hook Relay gives you Stripe-quality webhooks in minutes. Sign up for free today!

A Beginner's Guide to Exceptions in Ruby

The other day I was searching for an introduction to Ruby exceptions written for beginners - people who know basic Ruby syntax but aren't really sure what an exception is or why it's useful. I couldn't find one, so I decided to have a go at it myself. I hope you find it useful.

The other day I was searching for an introduction to Ruby exceptions written for beginners - people who know basic Ruby syntax but aren't really sure what an exception is or why it's useful. I couldn't find one, so I decided to have a go at it myself. I hope you find it useful. If there are any points that are confusing, feel free to tweet at me at @StarrHorne. :)

What's an exception?

Exceptions are Ruby's way of dealing with unexpected events.

If you've ever made a typo in your code, causing your program to crash with a message like SyntaxError or NoMethodError, then you've seen exceptions in action.

When you raise an exception in Ruby, the world stops and your program starts to shut down. If nothing stops the process, your program will eventually exit with an error message.

Here's an example. In the code below, we try to divide by zero. This is impossible, so Ruby raises an exception called ZeroDivisionError. The program quits and prints an error message.

1 / 0
# Program crashes and outputs: "ZeroDivisionError: divided by 0"

Crashing programs tend to make our users angry. So we normally want to stop this shutdown process, and react to the error intelligently.

This is called "rescuing," "handling," or "catching" an exception. They all mean the same thing. This is how you do it in Ruby:

  # Any exceptions in here... 
  # ...will cause this code to run
  puts "Got an exception, but I'm responding intelligently!"

# This program does not crash.
# Outputs: "Got an exception, but I'm responding intelligently!"

The exception still happens, but it doesn't cause the program to crash because it was "rescued." Instead of exiting, Ruby runs the code in the rescue block, which prints out a message.

This is nice, but it has one big limitation. It tells us "something went wrong," without letting us know what went wrong.

All of the information about what went wrong is going to be contained in an exception object.

Exception Objects

Exception objects are normal Ruby objects. They hold all of the data about "what happened" for the exception you just rescued.

To get the exception object, you will use a slightly different rescue syntax.

# Rescues all errors, an puts the exception object in `e`
rescue => e

# Rescues only ZeroDivisionError and puts the exception object in `e`
rescue ZeroDivisionError => e

In the second example above, ZeroDivisionError is the class of the object in e. All of the "types" of exceptions we've talked about are really just class names.

Exception objects also hold useful debug data. Let's take a look at the exception object for our ZeroDivisionError.

  # Any exceptions in here... 
rescue ZeroDivisionError => e
  puts "Exception Class: #{ e.class.name }"
  puts "Exception Message: #{ e.message }"
  puts "Exception Backtrace: #{ e.backtrace }"

# Outputs:
# Exception Class: ZeroDivisionError
# Exception Message: divided by 0
# Exception Backtrace: ...backtrace as an array...

Like most Ruby exceptions, it contains a message and a backtrace along with its class name.

Raising Your Own Exceptions

So far we've only talked about rescuing exceptions. You can also trigger your own exceptions. This process is called "raising." You do it by calling the raise method.

When you raise your own exceptions, you get to pick which type of exception to use. You also get to set the error message.

Here's an example:

  # raises an ArgumentError with the message "you messed up!"
  raise ArgumentError.new("You messed up!")
rescue ArgumentError => e  
  puts e.message

# Outputs: You messed up! 

As you can see, we're creating a new error object (an ArgumentError) with a custom message ("You messed up!") and passing it to the raise method.

This being Ruby, raise can be called in several ways:

# This is my favorite because it's so explicit
raise RuntimeError.new("You messed up!")

# ...produces the same result
raise RuntimeError, "You messed up!"

# ...produces the same result. But you can only raise 
# RuntimeErrors this way
raise "You messed up!"

Making Custom Exceptions

Ruby's built-in exceptions are great, but they don't cover every possible use case.

What if you're building a user system and want to raise an exception when the user tries to access an off-limits part of the site? None of Ruby's standard exceptions fit, so your best bet is to create a new kind of exception.

To make a custom exception, just create a new class that inherits from StandardError.

class PermissionDeniedError < StandardError


raise PermissionDeniedError.new()

This is just a normal Ruby class. That means you can add methods and data to it like any other class. Let's add an attribute called "action":

class PermissionDeniedError < StandardError

  attr_reader :action

  def initialize(message, action)
    # Call the parent's constructor to set the message

    # Store the action in an instance variable
    @action = action


# Then, when the user tries to delete something they don't
# have permission to delete, you might do something like this:
raise PermissionDeniedError.new("Permission Denied", :delete)

The Class Hierarchy

We just made a custom exception by subclassing StandardError, which itself subclasses Exception.

In fact, if you look at the class hierarchy of any exception in Ruby, you'll find it eventually leads back to Exception. Here, I'll prove it to you. These are most of Ruby's built-in exceptions, displayed hierarchically:


You don't have to memorize all of these, of course. I'm showing them to you because this idea of hierarchy is very important for a specific reason.

Rescuing errors of a specific class also rescues errors of its child classes.

Let's try that again...

When you rescue StandardError, you not only rescue exceptions with class StandardError but those of its children as well. If you look at the chart, you'll see that's a lot: ArgumentError, IOError, etc.

If you were to rescue Exception, you would rescue every single exception, which would be a very bad idea

Rescuing All Exceptions (the bad way)

If you want to get yelled at, go to stack overflow and post some code that looks like this:

// Don't do this 
rescue Exception => e

The code above will rescue every exception. Don't do it! It'll break your program in weird ways.

That's because Ruby uses exceptions for things other than errors. It also uses them to handle messages from the operating system called "Signals." If you've ever pressed "ctrl-c" to exit a program, you've used a signal. By suppressing all exceptions, you also suppress those signals.

There are also some kinds of exceptions — like syntax errors — that really should cause your program to crash. If you suppress them, you'll never know when you make typos or other mistakes.

Rescuing All Errors (The Right Way)

Go back and look at the class hierarchy chart and you'll see that all of the errors you'd want to rescue are children of StandardError

That means that if you want to rescue "all errors" you should rescue StandardError.

rescue StandardError => e
  # Only your app's exceptions are swallowed. Things like SyntaxErrror are left alone. 

In fact, if you don't specify an exception class, Ruby assumes you mean StandardError

rescue => e
  # This is the same as rescuing StandardError

Rescuing Specific Errors (The Best Approach)

Now that you know how to rescue all errors, you should know that it's usually a bad idea, a code smell, considered harmful, etc.

They're usually a sign that you were too lazy to figure out which specific exceptions needed to be rescued. And they will almost always come back to haunt you.

So take the time and do it right. Rescue specific exceptions.

rescue Errno::ETIMEDOUT => e
  // This will only rescue Errno::ETIMEDOUT exceptions

You can even rescue multiple kinds of exceptions in the same rescue block, so no excuses. :)

rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED => e

Honeybadger has your back when it counts.

We're the only error tracker that combines exception monitoring, uptime monitoring, and cron monitoring into a single, simple to use platform. Our mission: to tame production and make you a better, more productive developer.

Learn more
author photo

Starr Horne

Starr Horne is a Rubyist and Chief JavaScripter at Honeybadger.io. When she's not neck-deep in other people's bugs, she enjoys making furniture with traditional hand-tools, reading history and brewing beer in her garage in Seattle.

More articles by Starr Horne
“We've looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release.” 
Michael Smith
Try Error Monitoring Free for 15 Days
Are you using Bugsnag, Rollbar, or Airbrake for your monitoring? Honeybadger includes exception, uptime, and check-in monitoring — all for probably less than you're paying now. Discover why so many companies are switching to Honeybadger here.
Try Error Monitoring Free for 15 Days
Stop digging through chat logs to find the bug-fix someone mentioned last month. Honeybadger's built-in issue tracker keeps discussion central to each error, so that if it pops up again you'll be able to pick up right where you left off.
Try Error Monitoring Free for 15 Days
"Wow — Customers are blown away that I email them so quickly after an error."
Chris Patton
Try Error Monitoring Free for 15 Days