pcall
pcall calls a function in protected mode. If the function raises an error, pcall catches it and returns false with the error message. If the function succeeds, pcall returns true with the function’s return values.
Signature
pcall(f, arg1, arg2, ...)
Parameters:
f— the function to callarg1, arg2, ...— arguments to pass to the function
Returns: true, result1, result2, ... on success, or false, error_message on failure.
Basic Usage
The simplest way to use pcall is with a standard library function that you know might fail. The protected call wraps the operation so that any error becomes a return value instead of crashing your program. This is the foundation of Lua’s exception model — rather than try-catch blocks, Lua gives you pcall as a functional equivalent that fits with multiple return values.
Successful call:
ok, result = pcall(math.sqrt, 16)
print(ok) -- true
print(result) -- 4.0
The first return value is always a boolean indicating whether the call completed without an error. When ok is true, the remaining return values are whatever the wrapped function returned. This pattern means you never have to check for error objects unless you actually get a failure — the happy path reads cleanly without extra ceremony.
Call that raises an error:
ok, err = pcall(math.sqrt, -1)
print(ok) -- false
print(err) -- "math.sqrt: domain error"
When ok is false, the second return value is the error message — a string describing what went wrong. Unlike some languages where exceptions carry stack traces and type hierarchies, Lua errors in pcall are plain strings, though you can pass tables or other objects to error() for structured error information.
Why Use pcall
Without pcall, a runtime error crashes the entire script. Lua has no try-catch — if any function call fails, the error propagates up the call stack until it either hits a pcall boundary or terminates the host program.
-- crashes the whole program
result = unauthenticated_function()
print("this never runs")
With pcall, you isolate failures. The error stays contained within the protected call, and your program continues executing normally. This is the core mechanism behind Lua’s exception handling — pcall is the boundary between code that might fail and code that must keep running regardless.
ok, err = pcall(unauthenticated_function)
if not ok then
print("Function failed: " .. err)
-- handle gracefully, program continues
end
This is essential for user-provided code, plugin systems, API endpoints, and anywhere a failure should not terminate the host process. Game engines embedding Lua use pcall to safely execute user scripts. Web frameworks use it to wrap request handlers so one bad request does not bring down the server. Any environment where Lua is embedded in a larger application relies on pcall as the first line of defense against crashes.
Error handling patterns
Simple boolean check
The most common pattern is checking the first return value and branching on it. This works well for operations where you only need to know whether something succeeded, and the error message is sufficient for logging or displaying to the user.
ok, err = pcall(some_risky_operation)
if not ok then
log_error("Operation failed: " .. err)
end
This pattern is concise but only captures a single error message. If the wrapped function returns additional values on success, those values are lost when ok is false — pcall discards all return values beyond the first two when an error occurs. For functions with multiple return values that you need regardless of outcome, you may want to capture results before the pcall call instead.
Extracting error details
When you need both the error and whatever data was produced before the failure, wrap the call in an anonymous function. This lets you structure the return so that partial results are accessible alongside the error message.
ok, err = pcall(function()
return some_function(arg1, arg2)
end)
if not ok then
print("Failed: " .. tostring(err))
end
Wrapping in an anonymous function also lets you pass multiple arguments to the target function in a clean, readable way. You can also perform setup or validation inside the wrapper before calling the actual function, making this pattern more flexible than passing arguments directly to pcall.
Returning error to caller
Sometimes you want your own function to present a clean error interface to its callers. By using pcall internally and translating the result, you can convert Lua’s raw error strings into structured return values that follow your application’s conventions.
function safe_div(a, b)
ok, result = pcall(function() return a / b end)
if not ok then
return nil, "division failed: " .. result
end
return result
end
pcall vs xpcall
pcall catches only errors raised by the function itself — it does not catch errors in finalizers or metamethods that run during garbage collection. For more reliable error handling across all contexts, use xpcall, which lets you specify a custom error handler.
-- pcall does not catch this:
ok, err = pcall(setmetatable, {}, {
__gc = function() error("finalizer error") end
})
-- ok is true (pcall itself succeeds, GC error happens later)
The garbage collector runs at unpredictable times, so an error in a __gc metamethod can crash your program long after the pcall that created the object has returned. For objects with custom finalizers, you need xpcall with a Lua 5.4 __close metamethod or manual resource management to guarantee safe cleanup.
-- xpcall with handler:
ok, err = xpcall(function()
return some_function()
end, function(e)
return debug.traceback(e)
end)
-- err contains the full traceback
The error handler you pass to xpcall runs before the stack unwinds, giving you access to debug.traceback and other introspection functions that are unavailable after the stack has been popped. This is the key advantage over pcall — you get a full call-stack trace in your error report rather than just a single-line message.
Passing Arguments
Arguments are passed after the function. The first argument to pcall is always the function to call, and additional arguments are forwarded in order. This matches how xpcall and other higher-order functions work in Lua.
ok, result = pcall(string.format, "Hello %s", "world")
-- => true, "Hello world"
When the target function accepts multiple arguments, the forwarding mechanism handles all of them transparently. You can pass as many extra arguments as you need — pcall passes them all through without modification. This makes it straightforward to wrap functions that take configuration tables, callback references, or other structured data.
For functions with multiple return values, all are returned after the boolean. Lua functions commonly return several values at once, and pcall preserves this behavior — every return value from the wrapped function comes through after the success flag.
ok, r1, r2, r3 = pcall(some_function_with_4_returns)
Keep in mind that on failure, pcall returns only false and the error message — all partial results from the function are discarded. If you need intermediate data alongside error information, collect it before the pcall or use a closure that stores results in upvalues.
Common use cases
Safely evaluating user input
Loading and executing user-provided code is one of the most dangerous operations in any language. Lua’s load function compiles a string into a function, and pcall wraps the execution so that runtime errors in the user’s code do not crash your application.
function safe_eval(code)
local fn, err = load(code)
if not fn then
return nil, "Syntax error: " .. err
end
return pcall(fn)
end
The load step catches syntax errors before execution even begins, returning nil and a message if the code cannot be parsed. Once you have a valid function, pcall handles runtime errors like referencing undefined variables or calling functions that do not exist. Together, these two functions give you a complete sandbox for evaluating untrusted Lua code — though for production sandboxing you should also restrict the function’s environment using setfenv or the _ENV upvalue.
API request handling
When building web services or network-facing applications, every request handler is a potential crash point. Wrapping the handler in pcall ensures that a single malformed request does not take down the entire server.
function handle_request(request)
local ok, result = pcall(function()
return process_request(request)
end)
if not ok then
return { status = 500, error = tostring(result) }
end
return { status = 200, data = result }
end
This pattern converts Lua errors into HTTP-friendly responses. Successful processing returns 200, while any exception becomes a 500 with a message. You would typically also log with debug.traceback and include a request ID — but the core structure of pcall as error-to-response translator remains the same.
Optional library loading
Not every environment has every library available. When your code can work with or without a particular module, pcall with require lets you gracefully degrade instead of crashing.
local ok, socket = pcall(require, "socket")
if not ok then
print("Socket library not available: " .. socket)
-- fall back to alternative
end
This is especially common in Lua applications targeting multiple platforms — LuaJIT, PUC Lua, Roblox Luau — where libraries exist on some runtimes but not others. pcall(require, ...) lets you write code that adapts to available modules, falling back to pure-Lua alternatives when native libraries are absent.
See Also
- /reference/core-functions/ref-print/ — output values
- /reference/core-functions/ref-tonumber/ — convert to number
- /tutorials/lua-fundamentals/error-handling-basics/ — error handling patterns