Errors & Exceptions
16.1 Three classes of exceptions
error(Reason), runtime errors something went wrong (bug) — includes stack trace the process wants to stop non-local return, expected to be caught
16.2 Common runtime errors
wrong argument type to a BIF bad arithmetic (1 + a) {badmatch, V} failed = match no function clause matched {case_clause, V} no case branch matched no if branch true function doesn't exist / not exported calling a non-function {badarity,...} fun called with wrong number of args missing map key {badrecord, R} wrong record type atom table full, etc.
bad timeout in receive
16.3 try ... catch ... after
safe_divide(A, B) ->
try A / B of
Result -> {ok, Result} % optional "of" section: match on success
catch
error:badarith -> {error, division_by_zero};
error:Reason:Stacktrace -> % OTP 21+ stack trace syntax
logger:error("~p ~p", [Reason, Stacktrace]),
{error, Reason};
throw:Value -> {thrown, Value};
exit:Why -> {exited, Why};
Class:Reason -> {Class, Reason} % catch-all
after
io:format("cleanup always runs~n") % no return value
end.16.4 raising
error(my_error),
error({invalid_input, X}),
error(badarg, [Arg1]), % include args in the error report
exit(normal), exit(shutdown), exit({error, why}),
throw({found, Value}),
erlang:raise(Class, Reason, Stacktrace). % rethrow with original stacktrace16.5 Old-style catch
Ambiguous — prefer try.
catch 1 / 0. % {'EXIT', {badarith, [...]}}
catch throw(hello). % hello16.6 Error-handling philosophy
Return {ok, Value} / {error, Reason} for expected failures.
Crash (error/1, failed match) on unexpected conditions — let it crash.
Use throw for non-local returns within a module, never across APIs.
Assertive code: {ok, Fd} = file:open(F, [read]) crashes clearly if it fails.
16.7 Error information (OTP 24+)
erl_error:format_exception/3 and the error_info option give extended, human-readable error messages for BIFs.