» What is it?
The Exception class in Ruby is what all other errors inherit from; it's the top level exception, and it should very rarely — if ever — be rescued. In fact, some obscure bugs can actually be caused by rescuing Exception in the wrong place.
» Why you should never rescue Exception
Exception has a number of subclasses; some are recoverable while others are not. All recoverable errors inherit from the StandardError class, which itself inherits directly from Exception.
The other direct-descendants of Exception and their children are used by Ruby internally for other purposes. In many cases they are unrecoverable exceptions; the Ruby program cannot recover gracefully and must crash when they are encountered.
When you rescue Exception, you also rescue all of its children — including all of the unrecoverable exceptions:
begin
do_something_risky()
rescue Exception => e
# Don't do this. This will swallow every single exception. Nothing gets past it.
end
» Ruby's Internal Exceptions
There are only a handful of internal Exceptions that Ruby uses, and like we already mentioned, rescuing all of them indiscriminately is usually not a good idea (although rescuing some of them individually is fairly common). These are Ruby's internal exceptions.
» NoMemoryError
NoMemoryError is raised by Ruby when memory allocation fails. This does not mean that the system is out of memory; rather, it indicates that Ruby attempted to initialize an object that would consume more memory than the system allows.
On 64 bit systems, for example, the maximum allowed value of a String is 2**63 - 1. We can see that Ruby will not allow us to allocate a string longer than that:
s = String.new("1" * (2**63))
raises the exception:
RangeError: bignum too big to convert into `long'
However, while Ruby is capable of handling a String slightly shorter than the maximum, available memory is often a limiting factor:
s = String.new("1" * (2**62))
raises the exception:
NoMemoryError: failed to allocate memory
» LoadError
This exception is raised when a file required fails to load:
require 'these/are/not/the/scripts/youre/looking/for'
raises the exception:
LoadError: no such file to load -- these/are/not/the/scripts/youre/looking/for
A common pattern in Ruby is to rescue LoadError specifically (which is OK, since it's not rescuing any other exceptions) to load an optional dependency which may not be present:
being
require 'rack'
rescue LoadError
puts "Rack is not present, but it's OK because we expected it"
end
Because of this, if your Ruby application is crashing due to a missing constant belonging to a file which has been required, it's possible that the LoadError is being rescued improperly.
» NotImplementedError
NotImplementedError is raised when a feature is not implemented on the current platform. For example, if the underlying operating system or Ruby runtime doesn't support forking processes (via the fork system call), Ruby will raise NotImplementedError.
» SyntaxError
SyntaxError is raised when Ruby encounters code with an invalid syntax:
eval("puts('Forgot something)")
raises the following exception:
SyntaxError: (eval):1: unterminated string meets end of file
» SecurityError
SecurityError is part of Ruby's security model; it's raised when a potentially unsafe operation is attempted.
For example, every Ruby object that comes from an external source (such as a String read from a file, or an environment variable) is automatically marked as being "tainted". When your Ruby program works with tainted objects, it may raise the SecurityError exception when an operation is not permitted, such as attempting to eval a tainted String:
$SAFE = 1
foo = "puts 'hello world'"
foo.taint
eval(foo)
raises the exception:
SecurityError: Insecure operation - eval (SecurityError)
» Interrupt
Interrupt is raised when an interrupt signal is received from the operating system; typically when a user presses Control-C:
puts "Press CTRL-C to quit the loop"
loop {}
raises the exception:
Interrupt
» SystemExit
SystemExit is raised by exit to initiate the termination of the Ruby program:
begin
exit(1)
rescue SystemExit => e
puts "the ruby program is exiting with status #{e.status}"
raise
end
prints the output:
the ruby program is exiting with status 1
and exits with status 1.
If you rescue SystemExit without re-raising the exception, your Ruby program will continue to execute until it exits at the next opportunity, and the status code may be be different from the one that was rescued:
begin
exit(1)
rescue SystemExit => e
puts "the ruby program is exiting with status #{e.status}"
end
exit(0)
prints the output:
the ruby program is exiting with status 1
but exits with status 0.
» SystemStackError
SystemStackError is one of the more popular exceptions across all programming languages. If you've ever gotten a cryptic "stack level too deep" error in Ruby, you've encountered a SystemStackError exception:
def malcovitch_malcovitch
malcovitch_malcovitch
end
malcovitch_malcovitch
raises the exception:
SystemStackError: stack level too deep
» fatal
fatal is an Exception that Ruby raises when it encounters a fatal error and must exit. It is impossible to rescue fatal, and it's impossible to raise it artificially.
» If you must...
First of all, we'll say it again: don't rescue the Exception class in Ruby. If you must rescue Exception, such as for error reporting, always re-raise the exception promptly:
begin
do_something_risky()
rescue Exception => e
logger.fatal("Encountered an Exception: #{e}")
raise e # <-- always do this!
end
Fun fact: Rails 5.1 rescues Exception 37 times.