Elixir Cheatsheet
Error Handling
Use this Elixir reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
The {:ok, _} / {:error, _} Convention
case File.read("config.json") do {:ok, contents} -> parse(contents) {:error, :enoent} -> {:error, :missing_config} {:error, reason} -> {:error, reason} end
Expected failures return tagged tuples; exceptions are for bugs and truly unexpected states. Library pairs follow a naming rule: File.read/1 returns tuples, File.read!/1 (bang) returns the value or raises.
Chaining with with
with {:ok, body} <- File.read(path), {:ok, json} <- Jason.decode(body), %{"id" => id} <- json do {:ok, id} else {:error, %Jason.DecodeError{}} -> {:error, :bad_json} {:error, reason} -> {:error, reason} _ -> {:error, :invalid_shape} end
with is the idiomatic happy-path pipeline for functions that return tagged tuples; the first non-matching step falls through to else (or is returned as-is when there is no else).
raise and rescue
raise "boom" # RuntimeError raise ArgumentError, "expected a positive integer" try do String.to_integer(input) rescue e in ArgumentError -> {:error, Exception.message(e)} _e in [KeyError, MatchError] -> {:error, :bad_data} end
rescue matches exception structs by module. In most Elixir code try/rescue is rare — let it crash and supervise, or return tagged tuples.
try / after / else
{:ok, file} = File.open(path)
try do
IO.read(file, :eof)
after
File.close(file) # always runs (cleanup)
end
try do
compute(x)
rescue
ArithmeticError -> :error
else
result when result > 0 -> {:ok, result} # runs when no exception was raised
_ -> {:ok, 0}
endCustom Exceptions
defmodule PaymentError do defexception [:code, message: "payment failed"] end raise PaymentError, code: :card_declined # custom message from fields defmodule NotFoundError do defexception [:id] @impl true def message(%{id: id}), do: "record #{id} not found" end
defexception defines an exception struct (it is a struct with a :message contract). Convert any exception to text with Exception.message/1.
throw / catch and exit
try do Enum.each(1..100, fn x -> if found?(x), do: throw({:found, x}) end) :not_found catch {:found, x} -> {:ok, x} end exit(:normal) # terminate the current process exit({:shutdown, :maintenance}) try do GenServer.call(pid, :req) catch :exit, {:timeout, _} -> {:error, :timeout} # catch kind, reason end
throw is non-local return (rare; prefer Enum.reduce_while/3). exit signals process termination and is normally handled by supervisors, not caught.
Errors Across Processes
{:ok, pid} = Task.start_link(fn -> raise "boom" end) # crash propagates to linked parent
ref = Process.monitor(pid) # monitor: get a message instead
receive do
{:DOWN, ^ref, :process, _pid, reason} -> {:crashed, reason}
endThe OTP philosophy: don't defensively rescue everything — isolate risky work in a process, link or monitor it, and let supervisors restart clean state. See Processes and OTP.