January 8, 2012

Rescuing Exception

Over the past few months, I’ve been thinking about when it might be correct to say rescue Exception instead of rescue StandardError, or a more specific exception class. This line of thought was first triggered by a particularly hairy debug session which was made extremely difficult because, unbeknownst to me, some library code did a rescue Exception at the top-level of a Thread, where I was expecting Thread.abort_on_exception to explicitly break and tell me what was happening. The actual exception class was a SystemStackError, being triggered by a faulty recursive method. The above experience lead me to the axiom: Never Rescue Exception.

This highlights the problem with a vanilla rescue Exception: it’s way too broad. For writing day-to-day code, it’s very unlikely that you want to rescue the Exception subclasses which are not StandardErrors.

Since then, putting a couple more projects into production has made me think harder about this particular piece of dogma, and I’ve refined it somewhat:

  1. If you are writing a library, Never Rescue Exception.
  2. If you are writing an application (that is, your code is responsible for the top-level process), then Rescue Exception At Most Once.

If you write rescue Exception, here’s what you are claiming about your rescue clause:

  • You can handle running out of memory. (NoMemoryError)
  • You can handle misconfiguration of $LOAD_PATH. (LoadError)
  • Syntax errors don’t matter. (more ScriptError)
  • You’re responsible for low-level signal handling. (SignalException)
  • You control shutting down the process (SystemExit)
  • …and yes, you know what to do when any rogue method blows the stack. (SystemStackError)

Any time you actually need to handle any of these situations, you’ll definitely know about it, and in most cases all you will want to do is log an error somehow and exit(1).

There’s only one situation I can think of where it’s reasonable for a library to handle any of these, and that’s where that library is responsible for laoding plugin code. If you want to evaluate that code in the main interpreter (and that’s not unreasonable), then you definitely want to isolate the rest of the application from misbehaving plugins so that you can track down the problem more easily. However, in that case the correct exception to be rescuing is ScriptError, not Exception, so the rule still applies.