An exception is an object that represents the occurrence of an exceptional condition. In other words, it indicates that something went wrong.
In Ruby, exceptions are often referred to as errors. That's because the base Exception class exists as a top-level exception object element, but user-defined execution exceptions are generally StandardError or descendants.
To raise an exception use Kernel#raise passing the exception class and/or message:
raise StandardError # raises a StandardError.new
raise StandardError, "An error" # raises a StandardError.new("An error")
You can also simply pass an error message. In this case, the message is wrapped into a RuntimeError:
raise "An error" # raises a RuntimeError.new("An error")
Here's an example:
def hello(subject)
  raise ArgumentError, "`subject` is missing" if subject.to_s.empty?
  puts "Hello #{subject}"
end
hello # => ArgumentError: `subject` is missing
hello("Simone") # => "Hello Simone"
A custom exception is any class that extends Exception or a subclass of Exception.
In general, you should always extend StandardError or a descendant. The Exception family are usually for virtual-machine or system errors, rescuing them can prevent a forced interruption from working as expected.
# Defines a new custom exception called FileNotFound
class FileNotFound < StandardError
end
def read_file(path)
  File.exist?(path) || raise(FileNotFound, "File #{path} not found")
  File.read(path)
end
read_file("missing.txt")  #=> raises FileNotFound.new("File `missing.txt` not found")
read_file("valid.txt")    #=> reads and returns the content of the file
It's common to name exceptions by adding the Error suffix at the end:
ConnectionErrorDontPanicErrorHowever, when the error is self-explanatory, you don't need to add the Error suffix because would be redundant:
FileNotFound vs FileNotFoundErrorDatabaseExploded vs DatabaseExplodedErrorUse the begin/rescue block to catch (rescue) an exception and handle it:
begin
  # an execution that may fail
rescue
  # something to execute in case of failure
end
A rescue clause is analogous to a catch block in a curly brace language like C# or Java.
A bare rescue like this rescues StandardError.
Note: Take care to avoid catching Exception instead of the default StandardError. The Exception class includes SystemExit and NoMemoryError and other serious exceptions that you usually don't want to catch. Always consider catching StandardError (the default) instead.
You can also specify the exception class that should be rescued:
begin
  # an excecution that may fail
rescue CustomError
  # something to execute in case of CustomError
  # or descendant
end
This rescue clause will not catch any exception that is not a CustomError.
You can also store the exception in a specific variable:
begin
  # an excecution that may fail
rescue CustomError => error
  # error contains the exception
  puts error.message # provide human-readable details about what went wrong.
  puts error.backtrace.inspect # return an array of strings that represent the call stack
end
If you failed to handle an exception, you can raise it any time in a rescue block.
begin
   #here goes your code
rescue => e
    #failed to handle 
    raise e
end
If you want to retry your begin block, call retry:
begin
   #here goes your code
rescue StandardError => e
   #for some reason you want to retry you code
   retry
end
You can be stuck in a loop if you catch an exception in every retry. To avoid this, limit your retry_count to a certain number of tries.
retry_count = 0
begin
      # an excecution that may fail
rescue
    if retry_count < 5
        retry_count = retry_count + 1
        retry
    else
        #retry limit exceeds, do something else
    end
You can also provide an else block or an ensure block. An else block will be executed when the begin block completes without an exception thrown. An ensure block will always be executed. An ensure block is analogous to a finally block in a curly brace language like C# or Java.
begin
  # an execution that may fail
rescue
  # something to execute in case of failure
else
  # something to execute in case of success
ensure
  # something to always execute
end
If you are inside a def, module or class block, there is no need to use the begin statement.
def foo
    ...
rescue
    ...
end
You can handle multiple errors in the same rescue declaration:
begin
  # an execution that may fail
rescue FirstError, SecondError => e
  # do something if a FirstError or SecondError occurs
end
You can also add multiple rescue declarations:
begin
  # an execution that may fail
rescue FirstError => e
  # do something if a FirstError occurs
rescue SecondError => e
  # do something if a SecondError occurs
rescue => e
  # do something if a StandardError occurs
end
The order of the rescue blocks is relevant: the first match is the one executed. Therefore, if you put StandardError as the first condition and all your exceptions inherit from StandardError, then the other rescue statements will never be executed.
begin
  # an execution that may fail
rescue => e
  # this will swallow all the errors
rescue FirstError => e
  # do something if a FirstError occurs
rescue SecondError => e
  # do something if a SecondError occurs
end
Some blocks have implicit exception handling like def, class, and module. These blocks allow you to skip the begin statement.
def foo
    ...
rescue CustomError
    ...
ensure
    ...
end
It may be helpful to include additional information with an exception, e.g. for logging purposes or to allow conditional handling when the exception is caught:
class CustomError < StandardError
  attr_reader :safe_to_retry
  def initialize(safe_to_retry = false, message = 'Something went wrong')
    @safe_to_retry = safe_to_retry
    super(message)
  end
end
Raising the exception:
raise CustomError.new(true)
Catching the exception and accessing the additional information provided:
begin
  # do stuff
rescue CustomError => e
  retry if e.safe_to_retry
end