» 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.