# Exceptions

Ruby code can raise exceptions.

Most often, a raised exception is meant to alert the running program
that an unusual (i.e., _exceptional_) situation has arisen,
and may need to be handled.

Code throughout the Ruby core, Ruby standard library, and Ruby gems generates exceptions
in certain circumstances:

```rb
File.open('nope.txt') # Raises Errno::ENOENT: "No such file or directory"
```

## Raised Exceptions

A raised exception transfers program execution, one way or another.

### Unrescued Exceptions

If an exception not _rescued_
(see [Rescued Exceptions](#label-Rescued+Exceptions) below),
execution transfers to code in the Ruby interpreter
that prints a message and exits the program (or thread):

```console
$ ruby -e "raise"
-e:1:in '<main>': unhandled exception
```

### Rescued Exceptions

An <i>exception handler</i> may determine what is to happen
when an exception is raised;
the handler may _rescue_ an exception,
and may prevent the program from exiting.

A simple example:

```rb
begin
  raise 'Boom!'                # Raises an exception, transfers control.
  puts 'Will not get here.'
rescue
  puts 'Rescued an exception.' # Control transferred to here; program does not exit.
end
puts 'Got here.'
```

Output:

```
Rescued an exception.
Got here.
```

An exception handler has several elements:

| Element                     | Use                                                                                      |
|-----------------------------|------------------------------------------------------------------------------------------|
| Begin clause.               | Begins the handler and contains the code whose raised exception, if any, may be rescued. |
| One or more rescue clauses. | Each contains "rescuing" code, which is to be executed for certain exceptions.           |
| Else clause (optional).     | Contains code to be executed if no exception is raised.                                  |
| Ensure clause (optional).   | Contains code to be executed whether or not an exception is raised, or is rescued.       |
| <tt>end</tt> statement.     | Ends the handler.  `                                                                     |

#### Begin Clause

The begin clause begins the exception handler:

- May start with a `begin` statement;
  see also [Begin-Less Exception Handlers](#label-Begin-Less+Exception+Handlers).
- Contains code whose raised exception (if any) is covered
  by the handler.
- Ends with the first following `rescue` statement.

#### Rescue Clauses

A rescue clause:

- Starts with a `rescue` statement.
- Contains code that is to be executed for certain raised exceptions.
- Ends with the first following `rescue`,
  `else`, `ensure`, or `end` statement.

##### Rescued Exceptions

A `rescue` statement may include one or more classes
that are to be rescued;
if none is given, StandardError is assumed.

The rescue clause rescues both the specified class
(or StandardError if none given) or any of its subclasses;
see [Built-In Exception Class Hierarchy](rdoc-ref:Exception@Built-In+Exception+Class+Hierarchy).

```rb
begin
  1 / 0 # Raises ZeroDivisionError, a subclass of StandardError.
rescue
  puts "Rescued #{$!.class}"
end
```

Output:

```
Rescued ZeroDivisionError
```

If the `rescue` statement specifies an exception class,
only that class (or one of its subclasses) is rescued;
this example exits with a ZeroDivisionError,
which was not rescued because it is not ArgumentError or one of its subclasses:

```rb
begin
  1 / 0
rescue ArgumentError
  puts "Rescued #{$!.class}"
end
```

A `rescue` statement may specify multiple classes,
which means that its code rescues an exception
of any of the given classes (or their subclasses):

```rb
begin
  1 / 0
rescue FloatDomainError, ZeroDivisionError
  puts "Rescued #{$!.class}"
end
```

##### Multiple Rescue Clauses

An exception handler may contain multiple rescue clauses;
in that case, the first clause that rescues the exception does so,
and those before and after are ignored:

```rb
begin
  Dir.open('nosuch')
rescue Errno::ENOTDIR
  puts "Rescued #{$!.class}"
rescue Errno::ENOENT
  puts "Rescued #{$!.class}"
end
```

Output:

```
Rescued Errno::ENOENT
```

##### Capturing the Rescued \Exception

A `rescue` statement may specify a variable
whose value becomes the rescued exception
(an instance of Exception or one of its subclasses:

```rb
begin
  1 / 0
rescue => x
  puts x.class
  puts x.message
end
```

Output:

```
ZeroDivisionError
divided by 0
```

##### Global Variables

Two read-only global variables always have `nil` value
except in a rescue clause;
there:

- `$!`: contains the rescued exception.
- `$@`: contains its backtrace.

Example:

```rb
begin
  1 / 0
rescue
  p $!
  p $@
end
```

Output:

```
#<ZeroDivisionError: divided by 0>
["t.rb:2:in 'Integer#/'", "t.rb:2:in '<main>'"]
```

##### Cause

In a rescue clause, the method Exception#cause returns the previous value of `$!`,
which may be `nil`;
elsewhere, the method returns `nil`.

Example:

```rb
begin
  raise('Boom 0')
rescue => x0
  puts "Exception: #{x0.inspect};  $!: #{$!.inspect};  cause: #{x0.cause.inspect}."
  begin
    raise('Boom 1')
  rescue => x1
    puts "Exception: #{x1.inspect};  $!: #{$!.inspect};  cause: #{x1.cause.inspect}."
    begin
      raise('Boom 2')
    rescue => x2
      puts "Exception: #{x2.inspect};  $!: #{$!.inspect};  cause: #{x2.cause.inspect}."
    end
  end
end
```

Output:

```
Exception: #<RuntimeError: Boom 0>;  $!: #<RuntimeError: Boom 0>;  cause: nil.
Exception: #<RuntimeError: Boom 1>;  $!: #<RuntimeError: Boom 1>;  cause: #<RuntimeError: Boom 0>.
Exception: #<RuntimeError: Boom 2>;  $!: #<RuntimeError: Boom 2>;  cause: #<RuntimeError: Boom 1>.
```

#### Else Clause

The `else` clause:

- Starts with an `else` statement.
- Contains code that is to be executed if no exception is raised in the begin clause.
- Ends with the first following `ensure` or `end` statement.

```rb
begin
  puts 'Begin.'
rescue
  puts 'Rescued an exception!'
else
  puts 'No exception raised.'
end
```

Output:

```
Begin.
No exception raised.
```

#### Ensure Clause

The ensure clause:

- Starts with an `ensure` statement.
- Contains code that is to be executed
  regardless of whether an exception is raised,
  and regardless of whether a raised exception is handled.
- Ends with the first following `end` statement.

```rb
def foo(boom: false)
  puts 'Begin.'
  raise 'Boom!' if boom
rescue
  puts 'Rescued an exception!'
else
  puts 'No exception raised.'
ensure
  puts 'Always do this.'
end

foo(boom: true)
foo(boom: false)
```

Output:

```
Begin.
Rescued an exception!
Always do this.
Begin.
No exception raised.
Always do this.
```

#### End Statement

The `end` statement ends the handler.

Code following it is reached only if any raised exception is rescued.

#### Begin-Less \Exception Handlers

As seen above, an exception handler may be implemented with `begin` and `end`.

An exception handler may also be implemented as:

- A method body:

    ```rb
    def foo(boom: false) # Serves as beginning of exception handler.
      puts 'Begin.'
      raise 'Boom!' if boom
    rescue
      puts 'Rescued an exception!'
    else
      puts 'No exception raised.'
    end                  # Serves as end of exception handler.
    ```

- A block:

    ```rb
    Dir.chdir('.') do |dir| # Serves as beginning of exception handler.
      raise 'Boom!'
    rescue
      puts 'Rescued an exception!'
    end                     # Serves as end of exception handler.
    ```

#### Re-Raising an \Exception

It can be useful to rescue an exception, but allow its eventual effect;
for example, a program can rescue an exception, log data about it,
and then "reinstate" the exception.

This may be done via the `raise` method, but in a special way;
a rescuing clause:

  - Captures an exception.
  - Does whatever is needed concerning the exception (such as logging it).
  - Calls method `raise` with no argument,
    which raises the rescued exception:

```rb
begin
  1 / 0
rescue ZeroDivisionError
  # Do needful things (like logging).
  raise # Raised exception will be ZeroDivisionError, not RuntimeError.
end
```

Output:

```
ruby t.rb
t.rb:2:in 'Integer#/': divided by 0 (ZeroDivisionError)
    from t.rb:2:in '<main>'
```

#### Retrying

It can be useful to retry a begin clause;
for example, if it must access a possibly-volatile resource
(such as a web page),
it can be useful to try the access more than once
(in the hope that it may become available):

```rb
retries = 0
begin
  puts "Try ##{retries}."
  raise 'Boom'
rescue
  puts "Rescued retry ##{retries}."
  if (retries += 1) < 3
    puts 'Retrying'
    retry
  else
    puts 'Giving up.'
    raise
  end
end
```

```
Try #0.
Rescued retry #0.
Retrying
Try #1.
Rescued retry #1.
Retrying
Try #2.
Rescued retry #2.
Giving up.
# RuntimeError ('Boom') raised.
```

Note that the retry re-executes the entire begin clause,
not just the part after the point of failure.

## Raising an \Exception

\Method Kernel#raise raises an exception.

## Custom Exceptions

To provide additional or alternate information,
you may create custom exception classes.
Each should be a subclass of one of the built-in exception classes
(commonly StandardError or RuntimeError);
see [Built-In Exception Class Hierarchy](rdoc-ref:Exception@Built-In+Exception+Class+Hierarchy).

```rb
class MyException < StandardError; end
```

## Messages

Every `Exception` object has a message,
which is a string that is set at the time the object is created;
see Exception.new.

The message cannot be changed, but you can create a similar object with a different message;
see Exception#exception.

This method returns the message as defined:

- Exception#message.

Two other methods return enhanced versions of the message:

- Exception#detailed_message: adds exception class name, with optional highlighting.
- Exception#full_message: adds exception class name and backtrace, with optional highlighting.

Each of the two methods above accepts keyword argument `highlight`;
if the value of keyword `highlight` is `true`,
the returned string includes bolding and underlining ANSI codes (see below)
to enhance the appearance of the message.

Any exception class (Ruby or custom) may choose to override either of these methods,
and may choose to interpret keyword argument <tt>highlight: true</tt>
to mean that the returned message should contain
[ANSI codes](https://en.wikipedia.org/wiki/ANSI_escape_code)
that specify color, bolding, and underlining.

Because the enhanced message may be written to a non-terminal device
(e.g., into an HTML page),
it is best to limit the ANSI codes to these widely-supported codes:

- Begin font color:

    | Color   | ANSI Code        |
    |---------|------------------|
    | Red     | <tt>\\e[31m</tt> |
    | Green   | <tt>\\e[32m</tt> |
    | Yellow  | <tt>\\e[33m</tt> |
    | Blue    | <tt>\\e[34m</tt> |
    | Magenta | <tt>\\e[35m</tt> |
    | Cyan    | <tt>\\e[36m</tt> |

<br>

- Begin font attribute:

    | Attribute | ANSI Code       |
    |-----------|-----------------|
    | Bold      | <tt>\\e[1m</tt> |
    | Underline | <tt>\\e[4m</tt> |

<br>

- End all of the above:

    | Color | ANSI Code       |
    |-------|-----------------|
    | Reset | <tt>\\e[0m</tt> |

It's also best to craft a message that is conveniently human-readable,
even if the ANSI codes are included "as-is"
(rather than interpreted as font directives).

## Backtraces

A _backtrace_ is a record of the methods currently
in the [call stack](https://en.wikipedia.org/wiki/Call_stack);
each such method has been called, but has not yet returned.

These methods return backtrace information:

- Exception#backtrace: returns the backtrace as an array of strings or `nil`.
- Exception#backtrace_locations: returns the backtrace as an array
  of Thread::Backtrace::Location objects or `nil`.
  Each Thread::Backtrace::Location object gives detailed information about a called method.

By default, Ruby sets the backtrace of the exception to the location where it
was raised.

The developer might adjust this by either providing +backtrace+ argument
to Kernel#raise, or using Exception#set_backtrace.

Note that:

- by default, both +backtrace+ and +backtrace_locations+ represent the same backtrace;
- if the developer sets the backtrace by one of the above methods to an array of
  Thread::Backtrace::Location, they still represent the same backtrace;
- if the developer sets the backtrace to a string or an array of strings:
  - by Kernel#raise: +backtrace_locations+ become +nil+;
  - by Exception#set_backtrace: +backtrace_locations+ preserve the original
    value;
- if the developer sets the backtrace to +nil+ by Exception#set_backtrace,
  +backtrace_locations+ preserve the original value; but if the exception is then
  reraised, both +backtrace+ and +backtrace_locations+ become the location of reraise.
