Error Handling
LuaLuaA lightweight, embeddable scripting language used as the primary scripting language in FiveM server development. uses protected calls rather than try/catch. Errors are values (usually strings).
error(message [, level])
Raises an error. level controls where the error is attributed:
error("something broke") -- level 1: current function
error("bad arg", 2) -- level 2: calling function
assert(v [, message])
Checks that v is truthy. Returns v if ok, raises error if not.
local f = assert(io.open("file.txt", "r")) -- error if file not found
assert(type(n) == "number", "expected number")
Returns all arguments on success (useful for passing through):
local a, b, c = assert(multi_return_func())
pcall(f [, arg1, ...])
Protected call. Calls f in protected mode.
local ok, result = pcall(function()
error("bad thing")
end)
print(ok) -- false
print(result) -- bad thing
Return values
true, f() return values...on successfalse, error_objecton failure
local ok, a, b = pcall(function() return 1, 2 end)
-- ok=true, a=1, b=2
local ok, err = pcall(function() error({code=42}) end)
-- ok=false, err={code=42} -- error can be any value
xpcall(f, msgh [, arg1, ...])
Like pcall but with a message handler to transform/process the error.
local ok, result = xpcall(
function() error({detail = "oops"}) end,
function(err)
-- Transform error (e.g., add traceback)
return debug.traceback(tostring(err), 2)
end
)
print(result) -- includes stack trace
Common pattern: error with traceback
function try(f, ...)
return xpcall(f, debug.traceback, ...)
end
local ok, err = try(function()
error("fail")
end)
if not ok then
print(err) -- full traceback
end
Error objects
In Lua 5.4LuaA lightweight, embeddable scripting language used as the primary scripting language in FiveM server development., errors can be any value, not just strings:
-- String error (traditional)
error("file not found")
-- Table error (structured)
error({code = 404, message = "file not found", path = "/data"})
-- Number error
error(42)
[!tip] When using structured errors, always
tostring()them before displaying:local ok, err = pcall(risky)if not ok thenprint("Error: " .. tostring(err))end
Protected calls across boundaries
In coroutines
local co = coroutine.create(function()
error("oops")
end)
local ok, err = coroutine.resume(co) -- catches error
In C API
if (lua_pcall(L, nargs, nresults, 0) != LUA_OK) {
const char *err = lua_tostring(L, -1);
// handle error
}
Common patterns
Safe require
local function safe_require(mod)
local ok, result = pcall(require, mod)
if ok then return result end
return nil, result -- return nil + error message
end
Retry with backoff
function retry(f, max_attempts, delay)
local attempt = 1
while true do
local ok, result = pcall(f)
if ok then return result end
if attempt >= max_attempts then
error("failed after " .. attempt .. " attempts: " .. tostring(result))
end
attempt = attempt + 1
-- simple busy-wait (use os.execute("sleep") in real code)
for _ = 1, delay * 1000 do end
delay = delay * 2 -- exponential backoff
end
end
Cleanup with pcall
function with_cleanup(f, cleanup)
local ok, result = pcall(f)
cleanup() -- always runs
if not ok then error(result) end
return result
end
Rethrow with context
function wrap_context(f, context)
return function(...)
local ok, err = pcall(f, ...)
if not ok then
error({context = context, original = err})
end
end
end
error in C API
// Raise error from C
luaL_error(L, "bad argument #%d to '%s', expected %s",
arg, funcname, expected);
// Check and raise
luaL_checkinteger(L, 1); -- checks arg 1 is integer, raises if not
luaL_checkstring(L, 2); -- checks arg 2 is string
luaL_argcheck(L, cond, arg, msg); -- custom condition
// Type error
luaL_typeerror(L, arg, "table"); -- "bad argument #1, expected table, got ..."
Summary
| Function | Catches errors | Message handler | Use case |
|---|---|---|---|
pcall | ✅ | ❌ | Basic protected call |
xpcall | ✅ | ✅ | Add traceback, transform errors |
assert | ❌ | ❌ | Quick validation, raise if nil/false |
error | ❌ | ❌ | Raise an error |
coroutine.resume | ✅ | ❌ | Errors in coroutines |