Skip to content

Errors

Structured error handling with categorization and retry metadata. Global errors table available without require.

-- Simple message (kind defaults to UNKNOWN)
local err = errors.new("something went wrong")
-- With kind, retryable, and details
local err = errors.new({
message = "user not found",
kind = errors.NOT_FOUND,
retryable = false,
details = {user_id = 123}
})

errors.new accepts either a string message or a table with at least a message field. The (kind, message) form is not supported.

Add context while preserving kind, retryable, and details:

local data, err = db.query("SELECT * FROM users")
if err then
return nil, errors.wrap(err, "failed to load users")
end
MethodReturnsDescription
err:kind()stringError category
err:message()stringError message
err:retryable()boolean/nilWhether operation can be retried
err:details()table/nilStructured metadata
err:stack()stringLua stack trace
tostring(err)stringFull representation
if errors.is(err, errors.INVALID) then
-- handle invalid input
end
-- Or compare directly
if err:kind() == errors.NOT_FOUND then
-- handle missing resource
end
ConstantUse Case
errors.NOT_FOUNDResource doesn’t exist
errors.ALREADY_EXISTSResource already exists
errors.INVALIDBad input or arguments
errors.PERMISSION_DENIEDAccess denied
errors.UNAVAILABLEService temporarily down
errors.INTERNALInternal error
errors.CANCELEDOperation was canceled
errors.CONFLICTResource state conflict
errors.TIMEOUTOperation timed out
errors.RATE_LIMITEDToo many requests
errors.UNKNOWNUnspecified error

Get structured call stack:

local stack = errors.call_stack(err)
if stack then
print("Thread:", stack.thread)
for _, frame in ipairs(stack.frames) do
print(frame.source .. ":" .. frame.line, frame.name)
end
end
Typically RetryableNot Retryable
TIMEOUTINVALID
UNAVAILABLENOT_FOUND
RATE_LIMITEDPERMISSION_DENIED
ALREADY_EXISTS
if err:retryable() then
-- safe to retry
end
local err = errors.new({
message = "validation failed",
kind = errors.INVALID,
details = {
errors = {
{field = "email", message = "invalid format"},
{field = "age", message = "must be positive"}
}
}
})
local details = err:details()
for _, e in ipairs(details.errors) do
print(e.field, e.message)
end