Exception vs StandardError

# RuntimeError < StandardError < Exception < Object

begin
  require 'does/not/exist'
rescue Exception => e
  "it is an Exception"
end
# => "it is an Exception"

begin
  require 'does/not/exist'
rescue StandardError => e
  "it is an Exception"
end
# => LoadError: cannot load such file -- does/not/exist

What is the difference in results between this

raise "Oups" rescue "Hi"

and this?

require 'does/not/exist' rescue "Hi"

And why the results are different. Let’s figure it out step by step.

It turns out that a rescue clause without an explicit Exception class will rescue all StandardErrors (and inherited ones).

I.e., if we just raise with some string, the plain rescue will catch it

def foo
  raise "Oups"
end
foo rescue "Hello"   #=> "Hello"

because the raise "some string" raises RuntimeError which inherits from StandardError.

But if we raise an Exception, it won’t catch it, and we will have it raised.

def foo
  raise Exception
end
foo rescue "Hello"   #=> Exception: Exception

Here is another good example from Ruby’s documentation:

require 'does/not/exist' rescue "Hi" # => LoadError

The rescue does not catch it because the LoadError is inherited from Exception (via ScriptError), not from StandardError.

LoadError < ScriptError < Exception

If you need to rescue any error type/exception, it can be done like this:

begin
  # any error type/exception
rescue Exception => e
  "I will catch all of you"
end

Let’s check this out by raising different types explicitly.

E.g., a descendant of Exception like LoadError < ScriptError < Exception:

begin
  raise LoadError
rescue Exception => e
  "caught"
end
# => "caught"

E.g., a descendant of StandardError like FiberError < StandardError:

begin
  raise FiberError
rescue Exception => e
  "caught"
end
# => "caught"

E.g., a descendant of RuntimeError like FrozenError < RuntimeError < StandardError:

begin
  raise FrozenError
rescue Exception => e
  "caught"
end
# => "caught"

And just plain raise:

begin
  raise "some explanation"
rescue Exception => e
  "caught"
end
# => "caught"

As you can see, no matter what type is raised, it is always caught with rescue Exception.

Here is a shortcut to remember:

  • StandardError – default for rescue
  • RuntimeError – default for raise
  • Exception - is a parent of all of them

Let your exceptions be all caught and and properly handled 👍