[ruby] Why is it bad style to `rescue Exception => e` in Ruby?

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.