Errors, pcall and assert

Lua · Practical · 10 min

Lua has no try/catch; knowing pcall is essential for robust scripts and plugins.

error(msg) raises an error. The message can be any value, including a table carrying structured data. With a string, Lua prepends the position (file:line:); pass a level as the second argument to blame the caller (error(msg, 2)) or 0 to add nothing.

assert(v, msg) raises msg when v is falsy and otherwise returns all its arguments, so local f = assert(io.open(path)) is idiomatic: io.open returns nil, message on failure and assert turns that into an error.

To catch errors call the function through pcall (protected call): local ok, result = pcall(f, arg1, arg2). On success ok is true followed by f's results; on failure ok is false and the second value is the error object. xpcall(f, handler, ...) also calls a message handler before the stack unwinds, which is where you grab debug.traceback.

Two error styles coexist in Lua code:

  • Return nil, errmsg for expected failures (file not found, bad input). The standard library does this.
  • Raise with error for bugs and unrecoverable situations.

For cleanup, Lua 5.4 adds to-be-closed variables: local f <close> = obj calls the value's __close metamethod when the block exits, normally or by error.

Example

local function parse_age(s)
  local n = tonumber(s)
  if not n then error({code = "EINVAL", input = s}) end
  if n < 0 then error("age must be >= 0", 2) end
  return n
end

local ok, err = pcall(parse_age, "abc")
print(ok, type(err), err.code)         --> false	table	EINVAL

ok, err = pcall(parse_age, "42")
print(ok, err)                         --> true	42

local f, msg = io.open("/no/such/file")
print(f, msg ~= nil)                   --> nil	true

ok, err = xpcall(function() local t = nil; return t.x end,
                 function(e) return "handled: " .. e end)
print(ok, err:match("attempt to index") ~= nil)  --> false	true

Remember

  • pcall(f, ...) returns true, results or false, err
  • assert(v, msg) passes values through or raises
  • Expected failures return nil, msg; bugs raise with error

Watch out for

  • Calling pcall(f(x)): that calls f outside the protection; write pcall(f, x).

Check yourself

  1. Which call protects f against errors when passing it the argument x?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.lua.org/manual/5.4/manual.html#pdf-pcall