Ruby Exception Handling

Ruby Exception Handling: Fatal Error

Today we finally come to the end of the journey through our Ruby Exception Handling series. The last, and perhaps most critical, error left to discuss is the Fatal Error, which indicates a severe crash in a Ruby application — something so dramatic that the process couldn’t recover from it through normal means.

There’s no time to waste, so let’s get on with our last article detailing the plethora of possible Ruby exceptions. In this piece we’ll explore the Fatal Error in more detail, looking at where it fits within the Ruby Exception class hierarchy, as well as showing a few code examples illustrating how these painful errors might occur. Let’s get to it!

The Technical Rundown

  • All Ruby exceptions are descendants of the Exception class, or a subclass therein.
  • Fatal Error is the direct descendant of the Exception class.

When Should You Use It?

Fatal Errors are a beast. Generally speaking, when a Fatal Error occurs it means the application has failed so horrendously that Ruby is unable to recover from the problem. This can occur for a variety of reasons but some typical cases might be process issues, memory problems, IO failures, and so forth. However, Ruby already has built-in errors specifically designed for such failures, so when one of those existing error types isn’t raised it’s usually because the application broke unexpectedly and the a Fatal Error occurs.

Consequently, it should come as no surprise that Fatal Errors effectively cannot be rescued or recovered from. When a Fatal Error occurs the Ruby application is generally going to be shutting down/killing the process.

That said, we can still play with Fatal Errors a bit, since the underlying object that Ruby uses is still just that: an object.

First we’ll start with the full code sample below, after which we’ll go over it in more detail.

We begin with a couple normal methods with begin-rescue blocks. In these two examples we’re trying to raise a fatal exception through invalid permission access to a directory:

In this case we have the accessible and inaccessible sub-directories. The accessible directory has normal read/write permissions for the executing user account, so there’s no problem opening the path to ...\accessible\data.csv and creating a new file with our data. That data.csv file looks as expected:

However, trying to perform the same creation and write operation to ...\inaccessible\data.csv produces an error because our executing user doesn’t have permission:

Unsurprisingly, this isn’t a Fatal Error at all. As it happens, it is very difficult to purposefully cause a Fatal Error in Ruby code (and for good reason). However, to see just what the Fatal Error exception object is like and to play with it yourself, we can perform a small trick to raise such an error.

Here in the #raise_fatal_error method we’re looping through all the objects stored within the ObjectSpace module. Specifically, we want to use the ObjectSpace::each_object method, which calls a code block that iterates over every living object known to the current Ruby process. In other words, we can use ObjectSpace::each_object to loop through every built-in Ruby object and try to pull out the one we want. In this case, we want the fatal exception object, so we return that object and assign it to our new fatal variable.

From there we can issue a standard raise call and pass in our own error message. Just to make sure our bases are covered we try to rescue our actual fatal object, but also all Exception objects as well, as a backup.

As it turns out, since fatal is itself an Exception type, we’re able to rescue it directly, as confirmed by the log output:

To get the most out of your own applications and to fully manage any and all Ruby Exceptions, check out the Airbrake Ruby exception handling tool, offering real-time alerts and instantaneous insight into what went wrong with your Ruby code, including integrated support for a variety of popular Ruby gems and frameworks.