Elixir Errors - Handling Runtime Exceptions

Learn how to raise and rescue errors in Elixir. Understand when to use exceptions for exceptional circumstances and explore custom error definitions.

Elixir Error Handling

Understanding Elixir Errors

Elixir errors, or exceptions, are designed for use in exceptional circumstances, not for regular control flow. They signal that something unexpected has occurred. The Elixir documentation lists built-in errors, such as ArgumentError, which are crucial for understanding potential issues. These are typically found in the official documentation, often categorized on the left side of reference pages.

Raising Runtime Errors

You can explicitly trigger a runtime error using the raise keyword. This is useful for signaling a problem that the program cannot recover from gracefully. For instance, raising a simple string will create a RuntimeError:

> raise "not fabulous enough"
** (RuntimeError) not fabulous enough

To raise a specific error type with a custom message, you can provide the error module and the message:

> raise ArgumentError, "I'm done. We're talking in circles."
#** (ArgumentError) I'm done. We're talking in circles.

Some errors accept specific options that need to be passed as a keyword list. Consulting the source code or documentation for these errors is essential:

> raise KeyError, key: "to car", term: "pocket"
#** (KeyError) key "to car" not found in: "pocket"

Defining Custom Errors

Elixir allows you to define your own custom error types by creating a module that includes the defexception macro. This enhances code clarity by providing specific error types for domain-specific problems:

defmodule LandWarWithRussiaError do
  defexception message: "Never."
end
> raise LandWarWithRussiaError
#** (LandWarWithRussiaError) Never.

Rescuing Errors

The try/rescue construct allows you to catch and handle specific errors that occur within a block of code. This is a way to manage exceptional situations and prevent them from crashing the entire application. You can specify which error types to rescue:

try do
  if false do
    raise "are we there yet?"
  else
    raise ArgumentError, "I'll pull this car around!"
  end
rescue
  e in RuntimeError  -> IO.puts "No!"
  e in ArgumentError -> IO.puts "That's it we're going home!"
end

Important Note: While try/rescue is available, Elixir offers more idiomatic ways to control program flow, such as using pattern matching, `case` statements, and return tuples (e.g., {:ok, value} or {:error, reason}). Errors should be reserved for truly exceptional and unexpected situations.