Ryan Davis’s Ruby QuickRef says (without explanation):

Don’t rescue Exception. EVER. or I will stab you.

Why not? What’s the right thing to do?

8 upvote
  flag
I know the answer, I'm just asking in the hopes that someone will write up a good answer, because I wasn't able to find a good one with a few minutes searching. So far none of the answers are really correct. – John
30 upvote
  flag
Then you probably could write your own? :) – Sergio Tulentsev
41 upvote
  flag
I'm very uncomfortable with the call to violence here. It's just programming. – Fuser97381
1 upvote
  flag
Take a look at this article in Ruby Exception with a nice Ruby Exception Hierarchy. – Atul Khanduri

6 Answers 11

Because this captures all exceptions. It's unlikely that your program can recover from any of them.

You should handle only exceptions that you know how to recover from. If you don't anticipate a certain kind of exception, don't handle it, crash loudly (write details to the log), then diagnose logs and fix code.

Swallowing exceptions is bad, don't do this.

up vote 1169 down vote accepted

Exception is the root of Ruby's exception hierarchy, so when you rescue Exception you rescue from everything, including subclasses such as SyntaxError, LoadError, and Interrupt.

Rescuing Interrupt prevents the user from using CTRLC to exit the program.

Rescuing SignalException prevents the program from responding correctly to signals. It will be unkillable except by kill -9.

Rescuing SyntaxError means that evals that fail will do so silently.

All of these can be shown by running this program, and trying to CTRLC or kill it:

loop do
  begin
    sleep 1
    eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
  rescue Exception
    puts "I refuse to fail or be stopped!"
  end
end

Rescuing from Exception isn't even the default. Doing

begin
  # iceberg!
rescue
  # lifeboats
end

does not rescue from Exception, it rescues from StandardError. You should generally specify something more specific than the default StandardError, but rescuing from Exception broadens the scope rather than narrowing it, and can have catastrophic results and make bug-hunting extremely difficult.


If you have a situation where you do want to rescue from StandardError and you need a variable with the exception, you can use this form:

begin
  # iceberg!
rescue => e
  # lifeboats
end

which is equivalent to:

begin
  # iceberg!
rescue StandardError => e
  # lifeboats
end

One of the few common cases where it’s sane to rescue from Exception is for logging/reporting purposes, in which case you should immediately re-raise the exception:

begin
  # iceberg?
rescue Exception => e
  # do some logging
  raise e  # not enough lifeboats ;)
end
104 upvote
  flag
so it's like catching Throwable in java – ratchet freak
upvote
  flag
What are your thoughts on this usage: //allinonescript.com/a/766228/513739 – Excalibur
10 upvote
  flag
@Excalibur If you’re re-raising the exception, then it’s fine since you’re not swallowing it, but just trying to know that it happened then letting it bubble up. Usually done for logging. – Andrew Marshall
45 upvote
  flag
This advice is good for a clean Ruby environment. But unfortunately a number of gems have created exceptions that directly descend from Exception. Our environment has 30 of these: e.g. OpenID::Server::EncodingError, OAuth::InvalidRequest, HTMLTokenizerSample. These are exceptions that you'd very much want to catch in standard rescue blocks. Unfortunately, nothing in Ruby prevents or even discourages gems from inheriting directly from Exception -- even the naming is unintuitive. – Jonathan Swartz
14 upvote
  flag
@JonathanSwartz Then rescue from those specific subclasses, not Exception. More specific is nearly always better and clearer. – Andrew Marshall
3 upvote
  flag
Andrew - there are many times you want to catch all standard exceptions. You mentioned one yourself - if you want to add some context to the message, then rethrow or log or airbrake it. – Jonathan Swartz
upvote
  flag
I actually managed to kill it with ctrl + C – Bloodcount
18 upvote
  flag
@JonathanSwartz - I would bug the gem creators to change what their exception inherits from. Personally, I like my gems to have all exceptions descend from MyGemException, so you could rescue that if you wanted. – Nathan Long
8 upvote
  flag
You can also ADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error, Mysql2::Error, ::ActiveRecord::JDBCError, SQLite3::Exception] and then rescue *ADAPTER_ERRORS => e – j_mcnally
7 upvote
  flag
I just found another example, why rescue Exception is bad: It rescued failures in our specs! There was even a should_not_receive in our code base, and the author apparently trusted the specs and thought, it's already implemented: But that method was in fact being called, just the failure was rescued :( – iGEL
2 upvote
  flag
So, now I just need to make sure our gems don't raise Exception or some super duper custom exception that is a direct subclass of Exception! – nroose

That's a specific case of the rule that you shouldn't catch any exception you don't know how to handle. If you don't know how to handle it, it's always better to let some other part of the system catch and handle it.

The real rule is: Don't throw away exceptions. The objectivity of the author of your quote is questionable, as evidenced by the fact that it ends with

or I will stab you

Of course, be aware that signals (by default) throw exceptions, and normally long-running processes are terminated through a signal, so catching Exception and not terminating on signal exceptions will make your program very hard to stop. So don't do this:

#! /usr/bin/ruby

while true do
  begin
    line = STDIN.gets
    # heavy processing
  rescue Exception => e
    puts "caught exception #{e}! ohnoes!"
  end
end

No, really, don't do it. Don't even run that to see if it works.

However, say you have a threaded server and you want all exceptions to not:

  1. be ignored (the default)
  2. stop the server (which happens if you say thread.abort_on_exception = true).

Then this is perfectly acceptable in your connection handling thread:

begin
  # do stuff
rescue Exception => e
  myLogger.error("uncaught #{e} exception while handling connection: #{e.message}")
    myLogger.error("Stack trace: #{backtrace.map {|l| "  #{l}\n"}.join}")
end

The above works out to a variation of Ruby's default exception handler, with the advantage that it doesn't also kill your program. Rails does this in its request handler.

Signal exceptions are raised in the main thread. Background threads won't get them, so there is no point in trying to catch them there.

This is particularly useful in a production environment, where you do not want your program to simply stop whenever something goes wrong. Then you can take the stack dumps in your logs and add to your code to deal with specific exception further down the call chain and in a more graceful manner.

Note also that there is another Ruby idiom which has much the same effect:

a = do_something rescue "something else"

In this line, if do_something raises an exception, it is caught by Ruby, thrown away, and a is assigned "something else".

Generally, don't do that, except in special cases where you know you don't need to worry. One example:

debugger rescue nil

The debugger function is a rather nice way to set a breakpoint in your code, but if running outside a debugger, and Rails, it raises an exception. Now theoretically you shouldn't be leaving debug code lying around in your program (pff! nobody does that!) but you might want to keep it there for a while for some reason, but not continually run your debugger.

Note:

  1. If you've run someone else's program that catches signal exceptions and ignores them, (say the code above) then:

    • in Linux, in a shell, type pgrep ruby, or ps | grep ruby, look for your offending program's PID, and then run kill -9 <PID>.
    • in Windows, use the Task Manager (CTRL-SHIFT-ESC), go to the "processes" tab, find your process, right click it and select "End process".
  2. If you are working with someone else's program which is, for whatever reason, peppered with these ignore-exception blocks, then putting this at the top of the mainline is one possible cop-out:

    %W/INT QUIT TERM/.each { |sig| trap sig,"SYSTEM_DEFAULT" }
    

    This causes the program to respond to the normal termination signals by immediately terminating, bypassing exception handlers, with no cleanup. So it could cause data loss or similar. Be careful!

  3. If you need to do this:

    begin
      do_something
    rescue Exception => e
      critical_cleanup
      raise
    end
    

    you can actually do this:

    begin
      do_something
    ensure
      critical_cleanup
    end
    

    In the second case, critical cleanup will be called every time, whether or not an exception is thrown.

19 upvote
  flag
Sorry, this is wrong. A server should never rescue Exception and do nothing but log it. That will make it unkillable except by kill -9. – John
1 upvote
  flag
answer amended. – Michael Slade
8 upvote
  flag
Your examples in note 3 are not equivilant, an ensure will run regardless of whether there's an exception raised or not, while the rescue will only run if an exception was raised. – Andrew Marshall
1 upvote
  flag
They're not /exactly/ equivalent but I can't figure out how to succinctly express the equivalence in a way that isn't ugly. – Michael Slade
3 upvote
  flag
Just add another critical_cleanup call after the begin/rescue block in the first example. I agree not the most elegant code, but obviously the second example is the elegant way of doing it, so a little inelegance is just part of the example. – gtd
1 upvote
  flag
"Don't even run that to see if it works." seems a bad advice for coding... On the contrary, I would advise you to run it, to see it fail and to understand by yourself how if fails, instead of blindly believing someone else. Great answer anyway :) – huelbois
1 upvote
  flag
"The objectivity of the author of your quote is questionable". The guy wrote minitest and a ton of other widely used gems. blog.zenspider.com/projects – beckah

Let's say you are in a car (running Ruby). You recently installed a new steering wheel with the over-the-air upgrade system (which uses eval), but you didn't know one of the programmers messed up on syntax.

You are on a bridge, and realize you are going a bit towards the railing, so you turn left.

def turn_left
  self.turn left:
end

oops! That's probably Not Good™, luckily, Ruby raises a SyntaxError.

The car should stop immediately - right?

Nope.

begin
  #...
  eval self.steering_wheel
  #...
rescue Exception => e
  self.beep
  self.log "Caught #{e}.", :warn
  self.log "Logged Error - Continuing Process.", :info
end

beep beep

Warning: Caught SyntaxError Exception.

Info: Logged Error - Continuing Process.

You notice something is wrong, and you slam on the emergency breaks (^C: Interrupt)

beep beep

Warning: Caught Interrupt Exception.

Info: Logged Error - Continuing Process.

Yeah - that didn't help much. You're pretty close to the rail, so you put the car in park (killing: SignalException).

beep beep

Warning: Caught SignalException Exception.

Info: Logged Error - Continuing Process.

At the last second, you pull out the keys (kill -9), and the car stops, you slam forward into the steering wheel (the airbag can't inflate because you didn't gracefully stop the program - you terminated it), and the computer in the back of your car slams into the seat in front of it. A half-full can of Coke spills over the papers. The groceries in the back are crushed, and most are covered in egg yolk and milk. The car needs serious repair and cleaning. (Data Loss)

Hopefully you have insurance (Backups). Oh yeah - because the airbag didn't inflate, you're probably hurt (getting fired, etc).


But wait! There's more reasons why you might want to use rescue Exception => e!

Let's say you're that car, and you want to make sure the airbag inflates if the car is going more than 5mph before stopping.

 begin 
    # do driving stuff
 rescue Exception => e
    self.airbags.inflate if self.speed >= 5.mph 
    raise
 end

Here's the exception to the rule: You can catch Exception only if you re-raise the exception. So, a better rule is to never swallow Exception, and always re-raise the error.

But adding rescue is both easy to forget in a language like Ruby, and putting a rescue statement right before re-raising an issue feels a little non-DRY. And you do not want to forget the raise statement. And if you do, good luck trying to find that error.

Thankfully, Ruby is awesome, you can just use the ensure keyword, which makes sure the code runs. The ensure keyword will run the code no matter what - if an exception is thrown, if one isn't, the only exception being if the world ends (or other unlikely events).

 begin 
    # do driving stuff
 ensure
    self.airbags.inflate if self.speed >= 5.mph 
 end

Boom! And that code should run anyways. The only reason you should use rescue Exception => e is if you need access to the exception, or if you only want code to run on an exception. And remember to re-raise the error. Every time. Or you will have 3 people stabbing you (including your boss).


TL;DR

Don't rescue Exception => e (and not re-raise the exception) - or you might drive off a bridge.

3 upvote
  flag
Hahahaha! This is a great answer. I'm shocked that no one has commented. You give a clear scenario that makes the whole thing really understandable. Cheers! :-) – James Milani
upvote
  flag
@JamesMilani Thank you! – Ben Aubin
1 upvote
  flag
+💯 for this answer. Wish I could upvote more than once! 😂 – engineerDave
upvote
  flag
Enjoyed your answer! – Atul Vaibhav

This will also hide bugs from you, for example if you mistyped a method name:

def my_fun
  "my_fun"
end

begin
 # you mistypped my_fun to my_func
 my_func # my_func()
rescue Exception
  # rescued NameError (or NoMethodError if you called method with parenthesis)
end

Not the answer you're looking for? Browse other questions tagged or ask your own question.