assert
assert stops execution and raises an error if its first argument is false or nil. It passes through the argument unchanged if the condition is truthy, making it useful for validating inputs and checking that functions return expected values.
Basic Usage
-- Stop if condition is false/nil
assert(true) -- passes
assert(1) -- passes (truthy)
assert(false) -- error: assertion failed!
assert(nil) -- error: assertion failed!
-- With a custom message
assert(x > 0, "x must be positive")
-- error: x must be positive
When assert fails, it raises an error with the message. If no message is provided, it defaults to “assertion failed!”.
Validating Function Return Values
This is the most common use case — checking that a function call succeeded:
local file = assert(io.open("data.txt", "r"))
-- Raises error if file cannot be opened
local result = assert(database.query("SELECT * FROM users"))
-- Raises error if query returns nil/false
The key insight: assert does not catch errors. It raises one when something is wrong. For catching errors from functions that might fail, use pcall or xpcall instead.
Multiple Return Values
assert returns all its arguments when the condition passes. This is useful for chaining:
local ok, result = assert(SomeFunction())
-- If SomeFunction() returns (nil, "error"), assert raises and never returns
local ok, err = assert(io.open("file.txt"))
-- ok = file object, err = nil
When assert succeeds, it returns the condition (truthy) followed by any additional arguments:
local data = assert(json.decode(text))
-- Returns decoded data if decode succeeds
Custom Error Messages
Always provide descriptive messages for debugging:
assert(user_id > 0, "user_id must be positive, got " .. tostring(user_id))
assert(type(data) == "table", "expected table, got " .. type(data))
assert(#players > 0, "at least one player required")
Type Checking
Use assert to validate types at function boundaries:
local function process_data(data)
assert(type(data) == "table", "data must be a table")
assert(type(data.name) == "string", "data.name must be a string")
assert(type(data.age) == "number", "data.age must be a number")
-- proceed with processing
end
When Not to Use assert
assert is for programmer errors — things that should never happen in correct code. Do not use it for:
User input validation:
-- WRONG: User input goes in, this raises a raw error
assert(tonumber(age), "Invalid age")
-- RIGHT: Handle the case gracefully
local age = tonumber(age_input)
if not age then
print("Please enter a valid number")
return
end
Expected failures:
-- WRONG: Expected case that calling code handles
assert(file_exists, "File not found")
-- RIGHT: Return nil/error for expected failures
local file = io.open(filename)
if not file then
return nil, "File not found"
end
assert vs error
| Situation | Use |
|---|---|
| Programmer error (invalid internal state) | assert |
| Expected failure (file missing, user bad input) | error with return |
| Catching errors from unsafe operations | pcall / xpcall |
See Also
- /reference/core-functions/ref-error/ — raise errors explicitly
- /reference/core-functions/ref-pcall/ — protected call that catches errors
- /reference/core-functions/ref-select/ — select elements from a vararg list