Sometimes our Ruby programs throw errors which we don’t have full control over,
such as network timeouts. So, we need to catch and handle those errors. To do
so, we need to choose an Exception
-descended class (or classes) to catch.
What’s the right granularity of that class?
The rescued class must descend from Exception
Ruby’s Exception
class hierarchy starts with Exception
. If we try to
raise
an object that is not an Exception
, Ruby complains.
begin
raise 1234.0
rescue => error
puts error.inspect
end
Results in:
#<TypeError: exception class/object expected>
The default is StandardError
By default, rescue
only catches things that are StandardError
s. To rescue
something that isn’t a StandardError
, we have to specify:
begin
raise Exception.new
rescue Exception => error
puts "Correct!"
end
Results in:
Correct!
Rescuing Exceptions is not idiomatic
We don’t want to rescue Exception
s, however. Exceptions that aren’t
StandardError
s are reserved for things like Interrupt
when we hit Ctrl-C,
and NoMemoryError
.
Exceptions that are StandardError
s are what a normal Ruby program are supposed
to use.
Best-case scenario
In the best-case scenario, we know exactly which error (or errors) can occur.
So, we restrict our rescue
statement to only the subset of errors that are out
of our control:
HTTP_ERRORS = [
EOFError,
Errno::ECONNRESET,
Errno::EINVAL,
Net::HTTPBadResponse,
Net::HTTPHeaderSyntaxError,
Net::ProtocolError,
Timeout::Error
]
begin
some.http.call
rescue *HTTP_ERRORS => error
notify_airbrake(error)
end
By isolating our rescue
statement to a subset of classes, we won’t be
overly-greedy and silently fail when something else goes wrong, such as some
returning a nil
value.
When that’s not possible
On occasion, that won’t be possible. In those cases, we explicitly use
StandardError
instead:
begin
some.unique.situation
rescue StandardError => error
notify_airbrake(error)
end
What’s next
If you found this useful, you might also enjoy: