-
Notifications
You must be signed in to change notification settings - Fork 1
Exceptions and Throw
This page describes the Exception class and some of its subclasses, how exception objects are manipulated in Ruby, and the throw/catch feature (which does not employ Exception objects at all).
For details on how exceptions can be caught, see Ruby Expressions.
Exception is the root class of all exceptions in Ruby - only instances of Exception and its subclasses can be raised as exceptions. Exception objects store the following state:
- A message, which describes the exception (e.g. "invalid value for x: y"), accessed by the
message,to_sand==methods and modified by theinitializeconstructor method - A backtrace, accessed by the
backtracemethod and modified by theset_backtracemethod
This Exception object state (and, in general, additional state stored by Exception subclasses defined by core/extension libraries) is stored in the object as special hidden instance variables - they can't be directly accessed in Ruby code, however they do get copied in cloning/duping operations.
Ruby generally does not access the state directly, rather it uses the accessor/mutator methods - you are not required to store the state using the default native facilities, and may override these methods to access and store state how and if you wish as long as you observe the method contracts. However, note how by default Exception#exception directly modifies internal state as described below.
The message method is expected to return a String instance, or an object that can be converted to one via to_str. It is used by Ruby (along with class name, the backtrace method, and perhaps other VM information) when printing out details of an unhandled exception that caused program termination.
The backtrace method is expected to return one of the following:
- An array of Strings, or String-convertible objects via
to_str, that each describe a level of the Ruby stack in the backtrace from the innermost level outwards - A single String or String-convertible object, which Ruby will treat as being wrapped in an Array.
-
nil, which means no backtrace is associated with this object
The set_backtrace method is expected to store a backtrace object which can be subsequently accessed by the backtrace method. When the Ruby VM invokes set_backtrace it passes a valid backtrace object (i.e. an array of Strings).
Ruby code can build its own backtrace via Thread#backtrace (note totally unrelated to Exception#backtrace).
When Ruby enters a rescue or ensure clause, the exception that caused the clause to be entered is stored in a hidden dynamic variable - the exception context. No exception context is defined for ensure clauses if the clause was entered through normal execution, due to a control transfer statement, or due to a thread kill instruction.
Ruby code can access the exception context, however it is scoped differently to regular dynamic variables and other types of Ruby scoping. An exception context is scoped to cover code executing within the instance of the rescue/ensure clause associated with the exception, and recursively any methods/blocks that are called from within that clause, until another rescue/ensure clause is nested within which defines its own exception context. This is a purely runtime, stack-based scoping - the lexical location of a particular line of code or the closures it has access to have no bearing on what (if any) exception context is in scope for that line.
Ruby implements an unusual pattern for raising exceptions. Methods for raising exceptions, such as Kernel#raise, don't directly accept an Exception object to be thrown - they accept a template object from which an exception will be provided, by calling the template's exception method. This feature is basically here to permit some syntactic conveniences as shown below.
Ruby methods for raising exceptions (such as Kernel#raise, Kernel#fail, Thread#raise) are composed of 2 phases:
- Create the exception
- Raise the exception
In the case of Thread#raise, the exception is created, but isn't raised directly - it will be raised by the targeted thread.
All of these methods may be invoked with arguments as follows:
raise
raise <string>
raise <template>, [ <argument> ], [ <backtrace> ]
The exception creation phase works as follows:
- With no arguments:
- If an exception context exists, an exception will be provided by invoking
.exception()on the exception context exception, with no arguments - Otherwise, an exception will be created via
RuntimeError.new- note that this creation is actually deferred and will be performed at the beginning of the exception raising stage - With a
<string>, which is either an instance of String or an object that can be converted to a String viato_str, an exception will be created viaRuntimeError.new(<string>) - With a
<template>, an exception will be provided by invoking<template>.exception()or, if<argument>is provided,<template>.exception(<argument>). If<backtrace>is provided, it will subsequently be stored on the exception via.set_backtrace(<backtrace>)
The exception raising phase works as follows:
- Invokes
.backtrace()on the exception to determine if it doesn't have a backtrace (i.e. returnsnil) - If so, creates a backtrace and invokes
.set_backtrace(<backtrace>)on the exception - Raises the exception
Any object can be an exception template (with one exception - nil), all it has to do is implement the exception method. exception is expected to return an instance of Exception (which may be the template object itself), and it's expected to take 1 optional argument, of any type, which is passed in directly from a Kernel#raise (or similar method) argument and is intended to serve as a template-interpreted parameter in the exception object creation process.
There are 2 default implementations of exception:
-
Exception::exception, which implements the default behaviour ofClass#new -
Exception.exception, which expects an optional single argument ofselfor a message, and either: - Returns itself, if called with no arguments, or with 1 argument which is itself - note that it is probably sensible to preserve this behaviour if you override
exception - Creates a clone of itself (with the default implementation of
Object#clone), and invokes the default implementation ofException#initializeon the new object withexception's arguments- Note that
initializeaccepts 1 optional argument and will reset the internal backtrace state tonil, and the internal message state to the argument (or nil if not provided)
- Note that
The default implementations of exception suggest, for example, the following constructs:
raise exc # Where exc is an Exception object, raises it directly
raise exc, exc, bt # Where bt is a backtrace, sets the backtrace on the exception and raises it
raise exc, "hello" # Raises a clone of exc with the message "hello" and a new backtrace
raise StandardError, "hello" # Raises a new instance of StandardError created with the argument "hello"
Note that an exception class's initialize method should be prepared to be invoked with 1 optional argument through the Exception::exception method, unless that class overrides its Exception::exception method for different behaviour.
In the course of execution, Ruby will throw an exception if certain resources are required and are not available:
-
NoMemoryError, if memory could not be allocated -
SystemStackError, if we have run out of stack to execute a subroutine (either Ruby VM stack or underlying C machine stack)
The execution raising process is slightly modified for these exceptions:
- A single global instance of
NoMemoryErrorandSystemStackErroris created by the Ruby VM at initialization and used henceforth. Ruby does not create new instances when raising these exceptions. - When the global instance of SystemStackError is raised (by the Ruby VM or otherwise):
- During the creation phase, it will be used directly, rather than being treated as a template (i.e.
.exception()will not be called), and it will not be assigned a backtrace even if requested - During the raising phase, it will not go through the usual
backtrace/set_backtraceprotocol, rather a simple string will be assigned to its internal backtrace state directly
When the Ruby VM is raising a NoMemoryError or, on some architectures, a machine-stack-related SystemStackError, the execution properties of the affected fiber are temporarily modified until the exception is actually raised - in particular while creating or raising the exception, or executing any "raise" event hooks as a result of the exception:
- Ruby will not re-raise the same type of exception again - further resource unavailable errors may be immediately fatal to the program
- The fiber will not do any thread interrupt processing
Fatal exceptions throw/catch accessing errinfo from ruby code
- Exception - The root exception class - all objects to be raised as exceptions must inherit from this class.
- NoMemoryError - Raised when memory allocation failed (out of memory)
- SystemStackError - Raised when ran out of C stack or Ruby VM stack, e.g. method or proc calls nested too deep
- SecurityError - Raised when Ruby security features are enabled, and a disallowed insecure operation was attempted
- SystemExit - Raised to instruct the Ruby VM to terminate, typically by the
Kernel#exitorKernel#abortmethods - SignalException - Raised when the Ruby VM receives an OS signal (other than the SIGINT signal) that it has been configured to raise as an exception
- Interrupt - Like SignalException, for the SIGINT signal
- ScriptError - A container for exceptions due to problems with Ruby code
- LoadError - Raised when the Ruby VM is unable to open a Ruby sourcefile or extension library that it is attempting to load
- SyntaxError - Raised when the Ruby VM encounters invalid code when trying to compile Ruby code, e.g. a Ruby sourcefile or eval string.
- NotImplementedError - Raised when functionality is invoked which is not available or not yet supported on the current platform
- StandardError - A container for "regular" exceptions, i.e. all exceptions other than the special circumstance exceptions described above. The
rescueclause catches StandardError by default. Application-defined exceptions should inherit from this class.- ArgumentError - Raised when invalid arguments were provided to a method (and there is not a more specific exception type that could be thrown instead)
- EncodingError - A container for exceptions related to problems with translating between different String character set encodings. Note that a failure due to invalid byte sequences when interpreting against a single encoding is raised as an ArgumentError.
- Encoding::CompatibilityError - Raised when an operation requires strings to be bytecode-compatible with different character sets, and they are not
- Encoding::ConverterNotFoundError - Raised when Ruby can't transcode between character encodings because the necessary converters are not available
- Encoding::InvalidByteSequenceError - Raised when Ruby can't transcode between character encodings due to invalid byte sequences in the source string
- Encoding::UndefinedConversionError - Raised when Ruby can't transcode between character encodings because the target encoding does not support a character in the source string
- FiberError - Raised for errors associated with creating and manipulating Fibers
- IOError - Raised for general errors associated with I/O operations, other than OS-raised errors which are raised as SystemCallErrors
- EOFError - Raised for end-of-file errors associated with I/O operations
- IndexError - Raised when an invalid index was specified, e.g. into an array
- KeyError - Raised when an invalid key was specified, e.g. into a hash table
- StopIteration - Raised when using external (pull-style) iteration with an Enumerator object when the iteration is complete. Rescued by
Kernel#loopso this method can be used as a convenience to convert from pull-style to push-style enumeration.
- LocalJumpError - Raised when a yield or control transfer instruction fails to resolve
- Math::DomainError - Raised when a math function is invoked with arguments outside its valid domain
- NameError - Raised for errors associated with identifiers specified in Ruby code - e.g. methods, variables or constants which are invalid, don't exist or are disallowed in the context (excluding those raised for NoMethodError)
- NoMethodError - Raised when attempting to call a method on an object and the call is not resolved, either directly or via
method_missing.method_missingis required to raise this exception when invoked with methods that it doesn't implement.
- NoMethodError - Raised when attempting to call a method on an object and the call is not resolved, either directly or via
- RangeError - Raised for general errors where a numerical argument is outside the valid range for the operation
- FloatDomainError - Raised when attempting to convert special floating-point values (such as Infininty and NaN) to other numeric types which don't support them
- RegexpError - Raised for invalid regular expressions
- RuntimeError - A generic exception class for errors not falling in other categories. This is the default exception class raised by e.g.
Kernel#raiseif no other exception class is specified. - SystemCallError
- Errno::*
- Errno::NOERROR
- ThreadError - Raised for errors associated with creating and manipulating Threads and Mutexes
- TypeError - Raised for general errors where an argument is not of the expected class
- ZeroDivisionError - Raised when attempting to divide by 0
A Ruby Language Reference
Copyright © by Michael Hore, 2016.
Introduction to This Document
Ruby Elements
- Classes and Modules
- Methods
- Blocks, Procs and Lambdas
- Execution Context and Closures
- Variables, Constants and Namespaces
- Types and Literals
- Ruby Expressions
- Operators
Syntax Grammar
Exceptions and Throw
Ruby Sourcefiles and Libraries
Multi Threading
Execution and Lifecycle