Skip to content

Lua Error Handling Guide — Protected Calls and Exception Patterns

DodaTech Updated 2026-06-28 2 min read

In this tutorial, you will learn about Lua Error Handling Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

Lua error handling centers on pcall (protected call) which executes a function in protected mode and returns success status plus results -- with xpcall adding a message handler for custom error processing before the stack unwinds.

Basic pcall

local function divide(a, b)
    if b == 0 then
        error("Division by zero")
    end
    return a / b
end

local ok, result = pcall(divide, 10, 0)
if ok then
    print("Result:", result)
else
    print("Error:", result)  --> Error: Division by zero
end

xpcall with Error Handler

local function errorHandler(err)
    print("Custom handler:", err)
    print(debug.traceback())
    return "Wrapped: " .. err
end

local ok, result = xpcall(function()
    error("Something broke")
end, errorHandler)

if not ok then
    print("Final:", result)
end

Raising Errors

-- error(message) -- raises an error
-- error(message, level) -- level 1 = caller, level 2 = caller's caller

function validatePositive(n)
    if type(n) ~= "number" then
        error("Expected number, got " .. type(n), 2)
    end
    if n <= 0 then
        error("Must be positive", 2)
    end
end

Assert Pattern

local function loadConfig(filename)
    local f = assert(io.open(filename, "r"), "Cannot open " .. filename)
    local content = assert(f:read("*all"), "Cannot read " .. filename)
    f:close()
    return content
end

-- assert returns the value on success, or raises on nil/false
local value = assert(tonumber("42"), "Not a number")

Try-Catch Pattern

local function try(func, catch)
    local ok, result = pcall(func)
    if not ok and catch then
        catch(result)
    end
    return ok, result
end

try(function()
    error("runtime error")
end, function(err)
    print("Caught:", err)
end)

Common Mistakes

1. Catching and ignoring errors

Always log or handle errors. Silent failures make debugging impossible.

2. Using error for control flow

Use error only for exceptional situations. Use return values for expected failure cases.

3. Not providing enough context in error messages

Include function name and relevant values: error("validateAge: invalid age " .. tostring(age)).

Practice Questions

1. What is the difference between pcall and xpcall? xpcall accepts a message handler function that processes the error before the stack unwinds.

2. How does error's second argument work? The level argument controls where the error message points. Level 1 (default) points to the function calling error. Level 2 points to the caller's caller.

3. What does assert do? assert(condition, message) returns condition if truthy, or raises an error with the message if falsy.

FAQ

{{< faq question="Can I catch a Lua error in C code?" >}} Yes. The Lua C API uses lua_pcall which mirrors pcall. The C code can check the return status and handle errors from Lua. {{< /faq >}}

{{< faq question="How do I get a stack trace from an error?" >}} Use xpcall with debug.traceback as the message handler: xpcall(func, debug.traceback). {{< /faq >}}

{{< faq question="What types can be thrown with error?" >}} Any Lua value: strings, tables, or userdata. Strings are the convention. Use tables for structured error data. {{< /faq >}}

What's Next

Now learn about advanced OOP patterns in Lua.

Topic Description Link
OOP Advanced Inheritance, mixins, patterns {{< ref "24-oop-advanced" >}}
Weak Tables Weak references and caches {{< ref "25-weak-tables" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro