The Perils of "rescue Exception"

Ruby’s standard exception handling looks like this.

  rescue => ex
    # do something with the error

This will catch all StandardErrors and its subclasses. A frequent issue I see in Ruby code is this:

  rescue Exception => ex
    # Apparently I REALLY want to know if this
    # block of code fails for any reason.

This is a BAD, BAD idea, dear reader, and here’s why. Ruby uses Exceptions for other things than your application code errors. One example is the Interrupt class which is a SignalException. Ruby sends this Exception to all threads so that when the process gets an INT/Ctrl-C signal, all the threads will unwind and the process will shutdown. If you rescue Exception, you will potentially catch this exception and ignore it, making your thread and process an unkillable computing zombie. Your only choice will be to pull out your kill -9 shotgun and aim for the head.

Here’s an example of a Ruby script you cannot shutdown gracefully. Run it and you’ll see exactly the behavior I’ve described.

while true
    sleep 5
    puts 'ping'
  rescue Exception => ex
    puts "Mmmmm, brains"

So remember, your application errors should be subclasses of StandardError and if you want to catch everything, just stick will plain old “rescue => ex”. Your application will behave better for it.

12 thoughts on “The Perils of "rescue Exception"”

  1. Indeed! Another thing I see is “rescue Object”, which is especially silly since Ruby will not allow you to raise an object not derived from Exception.

    For anyone interested in using Ruby exceptions more effectively, may I humbly suggest “Exceptional Ruby”?

  2. Mike,

    Does the ‘=>’ have a special meaning in ‘rescue => ex’. Obviously you usually see the hash rocket in hashes, so does it have another function that isn’t used very often, or is this just a special use just when rescuing.

  3. Amen, and amen. I discovered this myself after rescuing Exception in all of my daemons and then wondering why I couldn’t kill them. This is great advice.

  4. Here’s my theory. This mis-idiom issue is an error caused by over-generalization from a specific case.


    require 'pp'
      i= 0
      if i == 0
      raise "random runtime exception"
      p 'I should never get executed'
    rescue ZeroDivisionError => ex
      p 'I am rescuing only ZeroDivisionErrors!'
      p "saw an #{ex.class}"
      pp ex.backtrace
    pp ZeroDivisionError.ancestors

    With output:

    "I am rescuing only ZeroDivisionErrors!"
    "saw an ZeroDivisionError"
    ["./j:7:in `/'", "./j:7"]
    [ZeroDivisionError, StandardError, Exception, Object, PP::ObjectMixin, Kernel]

    I think the thought process is:

    "Oh, I handle a ZeroDivsionError or a RuntimeError by putting its name before the =>. I will handle a more general exception by putting 'Exception' in front of the =>."

  5. In some circumstances you do want to catch *all* the exceptions, for example the REPL loop in Pry shouldn’t die if it gets a TimeoutError or a LoadError.

    Our solution, made with ideas from Avdi’s Exceptional Ruby, was to define a module that matches all exceptions except for the very few we don’t want to catch:

    # As a REPL, we often want to catch any unexpected exceptions that may have
    # been raised; however we don’t want to go overboard and prevent the user
    # from exiting Pry when they want to.
    module RescuableException
    def self.===(exception)
    case exception
    # Catch when the user hits ^C (Interrupt < SignalException), and assume
    # that they just wanted to stop the in-progress command (just like bash etc.)
    when Interrupt
    # Don't catch signals (particularly not SIGTERM) as these are unlikely to be
    # intended for pry itself. We should also make sure that Kernel#exit works.
    when SystemExit, SignalException
    # All other exceptions will be caught.

    We then use "rescue RescuableException" in place of "rescue Exception".

  6. The only counterargument I’d make is that (sadly) some gems use their own exception classes that don’t descend from StandardError, which makes rescue => ex inadequate.

  7. Care to listen to a counter argument?
    (warning: Ruby n00b here)

    If I’m not mistaken it should be noted that this does not apply to all (perhaps not even most?) cases of exception handling.
    While the
    rescue => ex
    block design would be useful for cases of *finalized* exception handling (i.e., final handling of an error situation in local context), there are other cases where you would want to have local cleanup *and* have the exception bubble up (i.e., re-throw), to signal a murky situation to upper layers as well.
    In such cases IMHO
    rescue Exception => e

    is exactly the right thing to do to ensure that *all* exceptions, not only application-level, are intercepted properly and then passed on.

  8. Hi Mike,

    Can you explain how this post relates to your Logging and RetryJobs middleware in sidekiq which rescue Exception explicity? I assume it’s ok because they’re re-raising?

Comments are closed.