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

Working with Ruby exceptions outside of the rescue block

It's often useful to be able to get the most recent exception, even if your code doesn't control the lifecycle of that exception. In this post we explore a few of the ways to do this.

It's often useful to be able to get the most recent exception, even if your code doesn't control the lifecycle of that exception. Imagine that you want to add basic crash detection to your application. You'd like to log extra info about any crash that happens as a result of an uncaught exception.

The first step is to add a handler that's run whenever your application exits. It's super easy to do this via the Ruby kernel's at_exit method.

  puts "the app exited"

But how can we know if the exit callback was invoked as a result of an exception? Well, Ruby provides the cryptically named $! global variable. It contains the most recently raised exception that has occurred somewhere in the current call stack.

It's trivial to use $! to detect if the program is being exited due to an exception. It looks something like this:

at_exit do
 save_error_to_log($!) if $!         

The limitations of $!

Unfortunately, the $! method only works if the exception occurred somewhere in the current call stack. If you rescue an exception, then try to access $! outside of the rescue clause, you'll get nil.

 raise "x"       
 puts $! # => RuntimeError           

puts $! # => nil         

This means that $! is pretty useless inside of a shell like IRB. Often in IRB, I'll run a method and get an exception. Sometimes I'd like to get ahold of that exception object. But $! doesn't work for this.

irb(main):001:0> 1/0
ZeroDivisionError: divided by 0
    from (irb):1:in `/'
irb(main):002:0> $!
=> nil

Working around $! with PRY

PRY gets around the limitations of $! by adding its own local variable, _ex_. This variable contains the most recent uncaught exception.

[1] pry(main)> raise "hi"        
RuntimeError: hi         
from (pry):1:in `__pry__'        
[2] pry(main)> _ex_      
=> #<RuntimeError: hi>

The reason that PRY is able to do this is because there are not really any uncaught exceptions inside of PRY or IRB. The shell itself catches the exceptions and displays them as nicely-formatted error messages.

I've copied the relevant bits of the PRY source below. You can see that the code that evaluates your commands is wrapped inside of a begin/rescue/end block. When a rescuable exception occurs, PRY saves the exception to self.last_exception and it later gets assigned to _ex_.

# Excerpted from the PRY source at https://github.com/pry/pry/blob/623306966bfa86890ac182bc8375ec9699abe90d/lib/pry/pry_instance.rb#L273

  if !process_command_safely(line)
    @eval_string << "#{line.chomp}\n" if !line.empty? || !@eval_string.empty?
rescue RescuableException => e
  self.last_exception = e
  result = e

  Pry.critical_section do

Require English

Perhaps you find variable names like $! a little hard on the eyes? Fortunately, Ruby includes a module called "English" which provides english-language versions of many global variables which otherwise look like robot cusswords.

The synonym for $! is $ERROR_INFO. You can use it wherever you'd normally use $!.

require "English"

 raise "x"       
 puts $ERROR_INFO # => RuntimeError          

And although most of the other english equivalents have nothing whatsoever to do with the topic of this blog post, I'm including them for kicks. English variables are on the left. The originals are on the right.

$FS $;
$OFS $,
$RS $/
$ORS $\
$NR $.
$PID $$
$ARGV $*

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.

“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