Nanfeng

Notes on software development, code, and curious ideas

Compiling, Loading, and Handling Errors in Lua

Lua compiles source into an internal function before executing it. Dynamic loading is convenient for configuration, plugins, and generated code, but compilation and execution are separate failure points.

loadfile, dofile, and load

loadfile compiles a file and returns a function or an error message:

1
2
3
4
5
6
7
8
9
local chunk, err = loadfile("settings.lua", "t", {})
if not chunk then
error("cannot compile settings: " .. err)
end

local ok, result = pcall(chunk)
if not ok then
error("cannot run settings: " .. result)
end

dofile is a convenience function that loads and immediately executes a file, propagating errors. load compiles source from a string or reader function. In Lua 5.2 and later, its mode argument can restrict input to text ("t") and its environment argument controls global-name resolution.

Never load untrusted text with a powerful environment. Precompiled bytecode is not a portable interchange format and should not be accepted from untrusted sources.

Raising and catching errors

Use error(message, level) to raise an error and assert to validate a condition:

1
2
3
4
5
local function divide(a, b)
assert(type(a) == "number" and type(b) == "number", "numbers required")
assert(b ~= 0, "division by zero")
return a / b
end

pcall executes a function in protected mode. It returns true followed by results, or false followed by the error object:

1
2
local ok, value = pcall(divide, 10, 0)
if not ok then print("failed:", value) end

For a traceback, use xpcall with an error handler:

1
2
3
4
local ok, result = xpcall(
function() return divide(10, 0) end,
function(err) return debug.traceback(tostring(err), 2) end
)

Do not catch every error merely to ignore it. Protect boundaries where the program can recover, attach useful context, preserve the original cause, and ensure external resources are closed even when execution fails.

+