luaguides

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 call
  • arg1, arg2, ... — arguments to pass to the function

Returns: true, result1, result2, ... on success, or false, error_message on failure.

Basic Usage

Successful call:

ok, result = pcall(math.sqrt, 16)
print(ok)       -- true
print(result)   -- 4.0

Call that raises an error:

ok, err = pcall(math.sqrt, -1)
print(ok)       -- false
print(err)      -- "math.sqrt: domain error"

Why Use pcall

Without pcall, a runtime error crashes the entire script:

-- crashes the whole program
result = unauthenticated_function()
print("this never runs")

With pcall, you isolate failures:

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.

Error Handling Patterns

Simple Boolean Check

ok, err = pcall(some_risky_operation)
if not ok then
    log_error("Operation failed: " .. err)
end

Extracting Error Details

ok, err = pcall(function()
    return some_function(arg1, arg2)
end)

if not ok then
    print("Failed: " .. tostring(err))
end

Returning Error to Caller

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)
-- xpcall with handler:
ok, err = xpcall(function()
    return some_function()
end, function(e)
    return debug.traceback(e)
end)
-- err contains the full traceback

Passing Arguments

Arguments are passed after the function:

ok, result = pcall(string.format, "Hello %s", "world")
-- => true, "Hello world"

For functions with multiple return values, all are returned after the boolean:

ok, r1, r2, r3 = pcall(some_function_with_4_returns)

Common Use Cases

Safely Evaluating User Input

function safe_eval(code)
    local fn, err = load(code)
    if not fn then
        return nil, "Syntax error: " .. err
    end
    return pcall(fn)
end

API Request Handling

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

Optional Library Loading

local ok, socket = pcall(require, "socket")
if not ok then
    print("Socket library not available: " .. socket)
    -- fall back to alternative
end

See Also