class Exception - RDoc Documentation

# class Exception

Class `Exception` and its subclasses are used to indicate that an error or other problem has occurred, and may need to be handled. See Exceptions.

An `Exception` object carries certain information:

- The type (the exception’s class), commonly `StandardError`, `RuntimeError`, or a subclass of one or the other; see Built-In Exception Class Hierarchy.

- An optional descriptive message; see methods `::new`, `message`.

- Optional backtrace information; see methods `backtrace`, `backtrace_locations`, `set_backtrace`.

- An optional cause; see method `cause`.

## Built-In Exception Class Hierarchy¶ ↑

The hierarchy of built-in subclasses of class `Exception`:

- `NoMemoryError`

- `ScriptError`

  - `LoadError`

  - `NotImplementedError`

  - `SyntaxError`

- `SecurityError`

- `SignalException`

  - `Interrupt`

- `StandardError`

  - `ArgumentError`

    - `UncaughtThrowError`

  - `EncodingError`

  - `FiberError`

  - `IOError`

    - `EOFError`

  - `IndexError`

    - `KeyError`

    - `StopIteration`

      - `ClosedQueueError`

  - `LocalJumpError`

  - `NameError`

    - `NoMethodError`

  - `RangeError`

    - `FloatDomainError`

  - `RegexpError`

  - `RuntimeError`

    - `FrozenError`

  - `SystemCallError`

    - `Errno` (and its subclasses, representing system errors)

  - `ThreadError`

  - `TypeError`

  - `ZeroDivisionError`

- `SystemExit`

- `SystemStackError`

- `fatal`

### Public Class Methods

exception(message = nil) → self or new_exception

Returns an exception object of the same class as `self`; useful for creating a similar exception, but with a different message.

With `message` `nil`, returns `self`:

    x0 = StandardError.new('Boom') # => #<StandardError: Boom>
    x1 = x0.exception              # => #<StandardError: Boom>
    x0.__id__ == x1.__id__         # => true

With string-convertible object `message` (even the same as the original message), returns a new exception object whose class is the same as `self`, and whose message is the given `message`:

    x1 = x0.exception('Boom') # => #<StandardError: Boom>
    x0..equal?(x1)            # => false

new(message = nil) → exception click to toggle source

Returns a new exception object.

The given `message` should be a string-convertible object; see method `message`; if not given, the message is the class name of the new instance (which may be the name of a subclass):

Examples:

    Exception.new         # => #<Exception: Exception>
    LoadError.new         # => #<LoadError: LoadError> # Subclass of Exception.
    Exception.new('Boom') # => #<Exception: Boom>

    static VALUE
    exc_initialize(int argc, VALUE *argv, VALUE exc)
    {
        VALUE arg;

        arg = (!rb_check_arity(argc, 0, 1) ? Qnil : argv[0]);
        return exc_init(exc, arg);
    }

to_tty? → true or false click to toggle source

Returns `true` if exception messages will be sent to a terminal device.

    static VALUE
    exc_s_to_tty_p(VALUE self)
    {
        return RBOOL(rb_stderr_tty_p());
    }

### Public Instance Methods

self == object → true or false click to toggle source

Returns whether `object` is the same class as `self` and its `message` and `backtrace` are equal to those of `self`.

    static VALUE
    exc_equal(VALUE exc, VALUE obj)
    {
        VALUE mesg, backtrace;

        if (exc == obj) return Qtrue;

        if (rb_obj_class(exc) != rb_obj_class(obj)) {
            int state;

            obj = rb_protect(try_convert_to_exception, obj, &state);
            if (state || UNDEF_P(obj)) {
                rb_set_errinfo(Qnil);
                return Qfalse;
            }
            if (rb_obj_class(exc) != rb_obj_class(obj)) return Qfalse;
            mesg = rb_check_funcall(obj, id_message, 0, 0);
            if (UNDEF_P(mesg)) return Qfalse;
            backtrace = rb_check_funcall(obj, id_backtrace, 0, 0);
            if (UNDEF_P(backtrace)) return Qfalse;
        }
        else {
            mesg = rb_attr_get(obj, id_mesg);
            backtrace = exc_backtrace(obj);
        }

        if (!rb_equal(rb_attr_get(exc, id_mesg), mesg))
            return Qfalse;
        return rb_equal(exc_backtrace(exc), backtrace);
    }

backtrace → array or nil click to toggle source

Returns the backtrace (the list of code locations that led to the exception), as an array of strings.

Example (assuming the code is stored in the file named `t.rb`):

    def division(numerator, denominator)
      numerator / denominator
    end

    begin
      division(1, 0)
    rescue => ex
      p ex.backtrace
      # ["t.rb:2:in 'Integer#/'", "t.rb:2:in 'Object#division'", "t.rb:6:in '<main>'"]
      loc = ex.backtrace.first
      p loc.class
      # String
    end

The value returned by this method migth be adjusted when raising (see `Kernel#raise`), or during intermediate handling by `set_backtrace`.

See also `backtrace_locations` that provide the same value, as structured objects. (Note though that two values might not be consistent with each other when backtraces are manually adjusted.)

see Backtraces.

    static VALUE
    exc_backtrace(VALUE exc)
    {
        VALUE obj;

        obj = rb_attr_get(exc, id_bt);

        if (rb_backtrace_p(obj)) {
            obj = rb_backtrace_to_str_ary(obj);
            /* rb_ivar_set(exc, id_bt, obj); */
        }

        return obj;
    }

backtrace_locations → array or nil click to toggle source

Returns the backtrace (the list of code locations that led to the exception), as an array of `Thread::Backtrace::Location` instances.

Example (assuming the code is stored in the file named `t.rb`):

    def division(numerator, denominator)
      numerator / denominator
    end

    begin
      division(1, 0)
    rescue => ex
      p ex.backtrace_locations
      # ["t.rb:2:in 'Integer#/'", "t.rb:2:in 'Object#division'", "t.rb:6:in '<main>'"]
      loc = ex.backtrace_locations.first
      p loc.class
      # Thread::Backtrace::Location
      p loc.path
      # "t.rb"
      p loc.lineno
      # 2
      p loc.label
      # "Integer#/"
    end

The value returned by this method might be adjusted when raising (see `Kernel#raise`), or during intermediate handling by `set_backtrace`.

See also `backtrace` that provide the same value as an array of strings. (Note though that two values might not be consistent with each other when backtraces are manually adjusted.)

See Backtraces.

    static VALUE
    exc_backtrace_locations(VALUE exc)
    {
        VALUE obj;

        obj = rb_attr_get(exc, id_bt_locations);
        if (!NIL_P(obj)) {
            obj = rb_backtrace_to_location_ary(obj);
        }
        return obj;
    }

cause → exception or nil click to toggle source

Returns the previous value of global variable `$!`, which may be `nil` (see Global Variables):

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

Output:

    Exception: Boom 0;  $!: Boom 0;  cause: nil.
    Exception: Boom 1;  $!: Boom 1;  cause: Boom 0.
    Exception: Boom 2;  $!: Boom 2;  cause: Boom 1.

    static VALUE
    exc_cause(VALUE exc)
    {
        return rb_attr_get(exc, id_cause);
    }

detailed_message(highlight: false, **kwargs) → string click to toggle source

Returns the message string with enhancements:

- Includes the exception class name in the first line.

- If the value of keyword `highlight` is `true`, includes bolding and underlining ANSI codes (see below) to enhance the appearance of the message.

Examples:

    begin
      1 / 0
    rescue => x
      p x.message
      p x.detailed_message                  # Class name added.
      p x.detailed_message(highlight: true) # Class name, bolding, and underlining added.
    end

Output:

    "divided by 0"
    "divided by 0 (ZeroDivisionError)"
    "\e[1mdivided by 0 (\e[1;4mZeroDivisionError\e[m\e[1m)\e[m"

This method is overridden by some gems in the Ruby standard library to add information:

- DidYouMean::Correctable#detailed_message.

- ErrorHighlight::CoreExt#detailed_message.

- SyntaxSuggest#detailed_message.

An overriding method must be tolerant of passed keyword arguments, which may include (but may not be limited to):

- `:highlight`.

- `:did_you_mean`.

- `:error_highlight`.

- `:syntax_suggest`.

An overriding method should also be careful with ANSI code enhancements; see Messages.

    static VALUE
    exc_detailed_message(int argc, VALUE *argv, VALUE exc)
    {
        VALUE opt;

        rb_scan_args(argc, argv, "0:", &opt);

        VALUE highlight = check_highlight_keyword(opt, 0);

        extern VALUE rb_decorate_message(const VALUE eclass, VALUE emesg, int highlight);

        return rb_decorate_message(CLASS_OF(exc), rb_get_message(exc), RTEST(highlight));
    }

exception(message = nil) → self or new_exception click to toggle source

Returns an exception object of the same class as `self`; useful for creating a similar exception, but with a different message.

With `message` `nil`, returns `self`:

    x0 = StandardError.new('Boom') # => #<StandardError: Boom>
    x1 = x0.exception              # => #<StandardError: Boom>
    x0.__id__ == x1.__id__         # => true

With string-convertible object `message` (even the same as the original message), returns a new exception object whose class is the same as `self`, and whose message is the given `message`:

    x1 = x0.exception('Boom') # => #<StandardError: Boom>
    x0..equal?(x1)            # => false

    static VALUE
    exc_exception(int argc, VALUE *argv, VALUE self)
    {
        VALUE exc;

        argc = rb_check_arity(argc, 0, 1);
        if (argc == 0) return self;
        if (argc == 1 && self == argv[0]) return self;
        exc = rb_obj_clone(self);
        rb_ivar_set(exc, id_mesg, argv[0]);
        return exc;
    }

full_message(highlight: true, order: :top) → string click to toggle source

Returns an enhanced message string:

- Includes the exception class name.

- If the value of keyword `highlight` is true (not `nil` or `false`), includes bolding ANSI codes (see below) to enhance the appearance of the message.

- Includes the backtrace:

  - If the value of keyword `order` is `:top` (the default), lists the error message and the innermost backtrace entry first.

  - If the value of keyword `order` is `:bottom`, lists the error message the the innermost entry last.

Example:

    def baz
      begin
        1 / 0
      rescue => x
        pp x.message
        pp x.full_message(highlight: false).split("\n")
        pp x.full_message.split("\n")
      end
    end
    def bar; baz; end
    def foo; bar; end
    foo

Output:

    "divided by 0"
    ["t.rb:3:in 'Integer#/': divided by 0 (ZeroDivisionError)",
     "\tfrom t.rb:3:in 'Object#baz'",
     "\tfrom t.rb:10:in 'Object#bar'",
     "\tfrom t.rb:11:in 'Object#foo'",
     "\tfrom t.rb:12:in '<main>'"]
    ["t.rb:3:in 'Integer#/': \e[1mdivided by 0 (\e[1;4mZeroDivisionError\e[m\e[1m)\e[m",
     "\tfrom t.rb:3:in 'Object#baz'",
     "\tfrom t.rb:10:in 'Object#bar'",
     "\tfrom t.rb:11:in 'Object#foo'",
     "\tfrom t.rb:12:in '<main>'"]

An overriding method should be careful with ANSI code enhancements; see Messages.

    static VALUE
    exc_full_message(int argc, VALUE *argv, VALUE exc)
    {
        VALUE opt, str, emesg, errat;
        VALUE highlight, order;

        rb_scan_args(argc, argv, "0:", &opt);

        highlight = check_highlight_keyword(opt, 1);
        order = check_order_keyword(opt);

        {
            if (NIL_P(opt)) opt = rb_hash_new();
            rb_hash_aset(opt, sym_highlight, highlight);
        }

        str = rb_str_new2("");
        errat = rb_get_backtrace(exc);
        emesg = rb_get_detailed_message(exc, opt);

        rb_error_write(exc, emesg, errat, str, opt, highlight, order);
        return str;
    }

inspect → string click to toggle source

Returns a string representation of `self`:

    x = RuntimeError.new('Boom')
    x.inspect # => "#<RuntimeError: Boom>"
    x = RuntimeError.new
    x.inspect # => "#<RuntimeError: RuntimeError>"

    static VALUE
    exc_inspect(VALUE exc)
    {
        VALUE str, klass;

        klass = CLASS_OF(exc);
        exc = rb_obj_as_string(exc);
        if (RSTRING_LEN(exc) == 0) {
            return rb_class_name(klass);
        }

        str = rb_str_buf_new2("#<");
        klass = rb_class_name(klass);
        rb_str_buf_append(str, klass);

        if (RTEST(rb_str_include(exc, rb_str_new2("\n")))) {
            rb_str_catf(str, ":%+"PRIsVALUE, exc);
        }
        else {
            rb_str_buf_cat(str, ": ", 2);
            rb_str_buf_append(str, exc);
        }

        rb_str_buf_cat(str, ">", 1);

        return str;
    }

message → string click to toggle source

Returns `to_s`.

See Messages.

    static VALUE
    exc_message(VALUE exc)
    {
        return rb_funcallv(exc, idTo_s, 0, 0);
    }

set_backtrace(value) → value click to toggle source

Sets the backtrace value for `self`; returns the given `value`.

The `value` might be:

- an array of `Thread::Backtrace::Location`;

- an array of `String` instances;

- a single `String` instance; or

- `nil`.

Using array of `Thread::Backtrace::Location` is the most consistent option: it sets both `backtrace` and `backtrace_locations`. It should be preferred when possible. The suitable array of locations can be obtained from `Kernel#caller_locations`, copied from another error, or just set to the adjusted result of the current error’s `backtrace_locations`:

    require 'json'

    def parse_payload(text)
      JSON.parse(text)  # test.rb, line 4
    rescue JSON::ParserError => ex
      ex.set_backtrace(ex.backtrace_locations[2...])
      raise
    end

    parse_payload('{"wrong: "json"')
    # test.rb:4:in 'Object#parse_payload': unexpected token at '{"wrong: "json"' (JSON::ParserError)
    #
    # An error points to the body of parse_payload method,
    # hiding the parts of the backtrace related to the internals
    # of the "json" library

    # The error has both #backtace and #backtrace_locations set
    # consistently:
    begin
      parse_payload('{"wrong: "json"')
    rescue => ex
      p ex.backtrace
      # ["test.rb:4:in 'Object#parse_payload'", "test.rb:20:in '<main>'"]
      p ex.backtrace_locations
      # ["test.rb:4:in 'Object#parse_payload'", "test.rb:20:in '<main>'"]
    end

When the desired stack of locations is not available and should be constructed from scratch, an array of strings or a singular string can be used. In this case, only `backtrace` is affected:

    def parse_payload(text)
      JSON.parse(text)
    rescue JSON::ParserError => ex
      ex.set_backtrace(["dsl.rb:34", "framework.rb:1"])
      # The error have the new value in #backtrace:
      p ex.backtrace
      # ["dsl.rb:34", "framework.rb:1"]

      # but the original one in #backtrace_locations
      p ex.backtrace_locations
      # [".../json/common.rb:221:in 'JSON::Ext::Parser.parse'", ...]
    end

    parse_payload('{"wrong: "json"')

Calling `set_backtrace` with `nil` clears up `backtrace` but doesn’t affect `backtrace_locations`:

    def parse_payload(text)
      JSON.parse(text)
    rescue JSON::ParserError => ex
      ex.set_backtrace(nil)
      p ex.backtrace
      # nil
      p ex.backtrace_locations
      # [".../json/common.rb:221:in 'JSON::Ext::Parser.parse'", ...]
    end

    parse_payload('{"wrong: "json"')

On reraising of such an exception, both `backtrace` and `backtrace_locations` is set to the place of reraising:

    def parse_payload(text)
      JSON.parse(text)
    rescue JSON::ParserError => ex
      ex.set_backtrace(nil)
      raise # test.rb, line 7
    end

    begin
      parse_payload('{"wrong: "json"')
    rescue => ex
      p ex.backtrace
      # ["test.rb:7:in 'Object#parse_payload'", "test.rb:11:in '<main>'"]
      p ex.backtrace_locations
      # ["test.rb:7:in 'Object#parse_payload'", "test.rb:11:in '<main>'"]
    end

See Backtraces.

    static VALUE
    exc_set_backtrace(VALUE exc, VALUE bt)
    {
        VALUE btobj = rb_location_ary_to_backtrace(bt);
        if (RTEST(btobj)) {
            rb_ivar_set(exc, id_bt, btobj);
            rb_ivar_set(exc, id_bt_locations, btobj);
            return bt;
        }
        else {
            return rb_ivar_set(exc, id_bt, rb_check_backtrace(bt));
        }
    }

to_s → string click to toggle source

Returns a string representation of `self`:

    x = RuntimeError.new('Boom')
    x.to_s # => "Boom"
    x = RuntimeError.new
    x.to_s # => "RuntimeError"

    static VALUE
    exc_to_s(VALUE exc)
    {
        VALUE mesg = rb_attr_get(exc, idMesg);

        if (NIL_P(mesg)) return rb_class_name(CLASS_OF(exc));
        return rb_String(mesg);
    }