100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Error Handling in Lua (pcall/error)

Learn how Lua signals and catches errors using error(), pcall(), and xpcall(), instead of try/catch exceptions.

OOP & ModulesIntermediate9 min readJul 10, 2026
Analogies

Raising Errors with error()

Lua does not have try/catch; instead, the built-in error(message, level) function raises an error that immediately unwinds the call stack until something catches it. The message is usually a string but can be any Lua value (including a table, useful for structured error objects). The optional level argument controls whether position information (file:line) is prepended: level 1 (the default) points to where error() was called, level 2 points to the caller of the function that called error(), and level 0 suppresses position info entirely.

🏏

Cricket analogy: When a third umpire signals 'not out' via a instant on-field alert that halts play immediately, everything stops right there, the way error() immediately halts normal execution and unwinds the stack.

lua
local function withdraw(balance, amount)
  if amount > balance then
    error("insufficient funds", 2)  -- level 2: blame the caller's line
  end
  return balance - amount
end

local ok, result = pcall(withdraw, 100, 150)
if not ok then
  print("Transaction failed: " .. result)
else
  print("New balance: " .. result)
end

Catching Errors with pcall

pcall(f, ...) ("protected call") invokes function f with the given arguments inside a protected environment. It always returns at least a boolean: true plus f's normal return values if f completed without error, or false plus the error value if f raised one. This is the fundamental Lua idiom for "catching" errors — instead of a try/catch block, you wrap the risky call itself in pcall and branch on its first return value.

🏏

Cricket analogy: A team sending in a night-watchman before a risky over is like wrapping a risky call in pcall, absorbing a potential dismissal without losing your best batter to the failure.

xpcall and Custom Handlers

xpcall(f, handler, ...) works like pcall but takes an additional error handler function that runs while the stack is still unwinding, before the stack is fully discarded. This matters because it lets you call debug.traceback() inside the handler to capture a full stack trace, something you cannot reconstruct after pcall has already returned and the original call stack is gone. xpcall is the standard choice when you need detailed diagnostics (e.g. logging) rather than just a pass/fail result.

🏏

Cricket analogy: A stump-mic audio review that captures the exact sound at the moment of a controversial dismissal, before the moment is lost, mirrors xpcall's handler capturing a traceback while the stack still exists.

A common pattern combines both: local ok, err = xpcall(riskyFn, debug.traceback). If riskyFn errors, err will contain the error message plus a full stack trace string, which is invaluable for logging in production servers where you can't attach a debugger.

Non-String Error Values and assert

Because error() accepts any Lua value, libraries sometimes raise a table like error({code = 404, message = "not found"}) so calling code can programmatically inspect err.code after a pcall rather than pattern-matching a string. Separately, assert(v, message) is a shorthand that raises error(message) if v is false or nil, and otherwise returns all its arguments unchanged — it's commonly used to validate function arguments or the success of an I/O operation like assert(io.open("data.txt")), which raises immediately if the file couldn't be opened.

🏏

Cricket analogy: A DRS review returning a structured verdict object (ball-tracking data, impact zone, decision) instead of just 'out' or 'not out' as plain text mirrors error() raising a structured table instead of a string.

  • Lua has no try/catch; error(message, level) raises an error that unwinds the stack until caught.
  • pcall(f, ...) runs f protected, returning true, results... on success or false, errorValue on failure.
  • xpcall(f, handler, ...) adds a handler that runs before the stack unwinds, ideal for capturing debug.traceback().
  • error() can raise any Lua value, not just strings, enabling structured error objects with fields like code.
  • assert(v, message) raises error(message) when v is falsy, otherwise returns its arguments unchanged.
  • The level argument to error() controls whether position info blames the caller (2) or the error() call site (1, default).

Practice what you learned

Was this page helpful?

Topics covered

#Programming#LuaStudyNotes#ErrorHandlingInLuaPcallError#Error#Handling#Lua#Pcall#ErrorHandling#StudyNotes#SkillVeris