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 StandardErrors. 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 Exceptions, however. Exceptions that aren’t
StandardErrors are reserved for things like Interrupt when we hit Ctrl-C,
and NoMemoryError.
Exceptions that are StandardErrors 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: