Errors#
Lua has no exception keyword. Errors are raised with error
and caught with pcall (protected call). A call that runs
outside pcall propagates the error up the stack and prints
a message when it reaches the top.
/controls`. For the assert
family used heavily in tests, see Testing.
The model#
error(msg)raises.msgis conventionally a string; any value works.pcall(f, ...)callsfin protected mode and returnstrue, ...on success orfalse, msgon error. The stack does not unwind past thepcall.xpcall(f, handler, ...)ispcallplus a handler that runs with the original stack still intact, sodebug.tracebackreturns something useful.assert(v, msg)isif not v then error(msg or "assertion failed") end, the standard guard at the top of a function.
local function parse(s)
local n = tonumber(s)
if not n then error("not a number: " .. s, 2) end
return n
end
local ok, result = pcall(parse, "x")
if not ok then
io.stderr:write("parse failed: ", result, "\n")
end
The second argument to error is the level at which to
report the error. Level 1 (default) blames the line that called
error; level 2 blames the caller of the function containing
error. Operators use level 2 for input-validation errors so
the message points at the bad call site.
Returning, not raising#
Lua’s stdlib convention is to return nil plus an error string
on recoverable failure, and to raise only for programming
mistakes. io.open, os.execute, and most network calls
follow this.
local f, err = io.open("/etc/shadow", "r")
if not f then
io.stderr:write("open failed: ", err, "\n")
return
end
Follow the same convention in their own code:
return value on success, nil, "message" on a failure the
caller can plausibly recover from.
pcall#
pcall runs its first argument in protected mode. The call
returns true plus whatever the function returned on success,
or false plus the error value on failure.
local ok, a, b = pcall(function() return 1, 2 end)
--> ok = true, a = 1, b = 2
local ok, err = pcall(function() error("boom") end)
--> ok = false, err = "input:1: boom"
The error value carries Lua’s standard file:line: msg prefix
when it was raised with a string. Strip it manually if the
message is going to a user-facing surface.
xpcall#
xpcall is pcall with a handler that runs before the
stack unwinds. The standard handler is debug.traceback so the
caller gets a stack trace instead of a one-line message.
local function run()
deep()
end
local ok, err = xpcall(run, debug.traceback)
if not ok then
io.stderr:write(err, "\n")
end
In long-running services (OpenResty, Wireshark dissectors,
Neovim plugins) wrap every entry point in
xpcall so a bug in user code does not crash the host.
Re-raising#
Re-raise by calling error again with the captured value.
local ok, err = pcall(do_thing)
if not ok then
log.warn("retrying after: " .. tostring(err))
-- ...
error(err, 0) -- 0 means "use msg verbatim"
end
Level 0 keeps the original file:line: prefix that was
baked into the error message, so the re-raise looks like the
original failure site.
assert#
assert(v, msg) is the operator’s terse guard. If v is
truthy, the call returns v (and any extra args), so the
operator can chain it inline.
local f = assert(io.open(path, "r"), "cannot open " .. path)
local n = assert(tonumber(arg[1]), "expected a number")
assert raises with "assertion failed!" if no message is
supplied. The operator always supplies a message.
References#
Control flow for
returnand the surrounding jump surface.Testing for
assertinsidebusted/luaunitspecs.Functions for the call surface
pcallwraps.