luaguides

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

SituationUse
Programmer error (invalid internal state)assert
Expected failure (file missing, user bad input)error with return
Catching errors from unsafe operationspcall / xpcall

See Also