Error Handling

22.1 Philosophy

Use tagged tuples ({:ok, v} / {:error, reason}) for expected failures.

Use exceptions for unexpected situations / programmer errors.

Use "let it crash" + supervisors for process-level failures (§30–33).

Convention: fun returns tuples, fun! raises.

File.read("x")      # {:error, :enoent}
File.read!("x")     # ** (File.Error) could not read file "x": no such file or directory

22.2 Raising and rescuing

Functions can use implicit try:

raise "oops"                                  # RuntimeError
raise ArgumentError, message: "bad arg"
raise ArgumentError, "bad arg"

try do
  String.to_integer("abc")
rescue
  e in ArgumentError -> IO.puts("Invalid: #{Exception.message(e)}")
  e -> reraise e, __STACKTRACE__
else
  value -> value * 2            # runs if no exception
after
  IO.puts("always runs")        # cleanup
end
def safe_div(a, b) do
  a / b
rescue
  ArithmeticError -> :infinity
end

22.3 Custom exceptions

defmodule InsufficientFunds do
  defexception [:needed, message: "insufficient funds"]

  @impl true
  def message(%{needed: n}), do: "insufficient funds: need #{n} more"
end

raise InsufficientFunds, needed: 50

22.4 throw / catch (non-local return; rare)

try do
  Enum.each(1..100, fn x -> if x == 42, do: throw({:found, x}) end)
catch
  {:found, x} -> x
end

22.5 exit

try do
  exit(:shutdown)
catch
  :exit, reason -> {:exited, reason}
end

# Catch anything: kind is :error | :throw | :exit
try do
  risky()
catch
  kind, value -> {kind, value}
end

22.6 Common built-in exceptions

ArgumentError, ArithmeticError, KeyError, MatchError, FunctionClauseError, CaseClauseError, CondClauseError, WithClauseError, UndefinedFunctionError, Protocol.UndefinedError, RuntimeError, File.Error, Enum.EmptyError, BadMapError, BadStructError, SystemLimitError.

22.7 Error-handling patterns

# with for chains
with {:ok, a} <- step1(), {:ok, b} <- step2(a), do: {:ok, b}

# Normalizing errors
def fetch(id) do
  case Repo.get(User, id) do
    nil -> {:error, :not_found}
    user -> {:ok, user}
  end
end