Error Handling
Prerequisites: Structs
When something goes wrong, Julia throws an exception. Catch it with
try/catch:
function safe_divide(a, b)
try
return div(a, b)
catch err
println("caught: ", sprint(showerror, err))
return nothing
finally
println("done dividing $a by $b")
end
end
println(safe_divide(10, 2))
println(safe_divide(1, 0))
done dividing 10 by 2
5
caught: DivideError: integer division error
done dividing 1 by 0
nothing
catch errbinds the exception toerr.finallyalways runs — use it for cleanup.sprint(showerror, err)turns an exception into its message.
Throwing errors
function withdraw(balance, amount)
amount > 0 || throw(ArgumentError("amount must be positive"))
amount <= balance || error("insufficient funds: balance is $balance")
return balance - amount
end
for amount in (30, -5, 500)
try
println("new balance: ", withdraw(100, amount))
catch err
println(typeof(err), ": ", err.msg)
end
end
new balance: 70
ArgumentError: amount must be positive
ErrorException: insufficient funds: balance is 100
throw(SomeException(...))throws a specific exception type.error("message")throws a genericErrorException.- Built-in types include
ArgumentError,DomainError,BoundsError,KeyError,MethodError,DimensionMismatch, andInexactError.
Custom exceptions and catching by type
Julia's catch doesn't filter by type, so check the type yourself and
rethrow() what you don't handle:
struct ValidationError <: Exception
field::String
reason::String
end
Base.showerror(io::IO, e::ValidationError) = print(io, "invalid ", e.field, ": ", e.reason)
function register(email)
occursin('@', email) || throw(ValidationError("email", "missing @"))
return "registered $email"
end
function try_register(email)
try
println(register(email))
catch err
err isa ValidationError || rethrow()
println("Error: ", sprint(showerror, err))
end
end
try_register("ada@example.com")
try_register("not-an-email")
registered ada@example.com
Error: invalid email: missing @
Errors versus nothing
Exceptions are for unexpected problems. For expected "no result"
cases, return nothing and let the caller check — like tryparse,
findfirst, and match do. Also, try/catch is relatively slow, so
don't use it for normal control flow in hot loops.
@assert
@assert cond "message" throws an AssertionError if cond is false.
Use it for internal sanity checks, not for validating user input (it may
be disabled at higher optimization levels in the future).
Practice
Write parse_age(s) that returns an Int or throws an ArgumentError
with a helpful message for non-numeric input or ages outside 0–150.
Test it on "42", "abc", and "200".
Show solution
function parse_age(s)
n = tryparse(Int, s)
n === nothing && throw(ArgumentError("'$s' is not a number"))
0 <= n <= 150 || throw(ArgumentError("$n is not a realistic age"))
return n
end
for s in ["42", "abc", "200"]
try
println(parse_age(s))
catch err
println(err.msg)
end
end
42
'abc' is not a number
200 is not a realistic age