luaguides

loadfile

loadfile ([filename [, mode [, env]]])

Overview

loadfile reads a Lua chunk from a file on disk, compiles it, and hands it back as a function. It does not run the chunk; that is your job, with a call to the returned value. The Lua 5.4 manual puts it this way: “Similar to load, but gets the chunk from file filename or from the standard input, if no file name is given.”

That separation between compiling and executing is the whole reason loadfile exists. It lets you inspect a file’s syntax, swap out its environment, defer execution, or wrap the call in pcall before running it. If you do not need any of that, dofile does the same job in one step and is shorter to type.

Signature

loadfile ([filename [, mode [, env]]])

All three arguments are optional. With no arguments at all, loadfile reads a chunk from standard input.

Parameters

filename

A string path to the file to load, or nil. When omitted or nil, the chunk is read from stdin until end-of-file. The path is also used as the chunk’s name in error messages and stack traces. There is no separate chunkname parameter, unlike load.

local chunk, err = loadfile("modules/payments.lua")

mode

A string that controls which chunk formats are accepted:

ValueBehavior
"t"Text only. Reject precompiled binary chunks.
"b"Binary only. Reject text chunks.
"bt"Both. Default.

The default "bt" is fine for trusted code, but it is worth being explicit when loading anything you did not write yourself. Binary chunks produced by string.dump or luac are not verified beyond the outer wrapper. The Lua manual warns that “running maliciously crafted bytecode can crash the interpreter.” For untrusted input, pass mode = "t".

env

A table that becomes the chunk’s _ENV upvalue. The chunk’s globals resolve through this table, which is how you sandbox a script: pass a stripped-down environment with only the functions it is allowed to call.

local chunk = assert(loadfile("plugin.lua", "t", safe_env))

If env is omitted, the chunk uses the calling Lua state’s global environment.

Return Values

loadfile returns one of two shapes, matching load:

  • On success, the compiled chunk as a function.
  • On any failure (file not found, read error, syntax error, malformed binary): nil plus an error message string.

It does not raise. That is deliberate: you can check the error and decide what to do, instead of having it propagate up. If you want it to raise, wrap the call in assert:

local chunk = assert(loadfile("greet.lua"))   -- raises with the error string on failure

Examples

Load a file and call the chunk

The two files below show the standard split. greet.lua defines a function and returns it; main.lua loads the file, runs the chunk, and uses whatever the chunk returns.

greet.lua:

-- greet.lua
local function greet(name)
    return "hello, " .. name
end
return greet

main.lua is the consumer. It calls loadfile, checks the return value, then runs the chunk to get the function back, and finally calls it with an argument.

main.lua:

-- main.lua
local chunk, err = loadfile("greet.lua")
if not chunk then
    error("failed to load greet.lua: " .. tostring(err))
end

local greet = chunk()      -- run the chunk; it returns a function
print(greet("world"))      -- -> hello, world

The double-call pattern trips up a lot of people. chunk is the loaded file as a function; chunk() is the file executing and returning whatever the file’s top-level return produced.

Sandbox a script with a custom environment

-- sandbox.lua
local env = {
    print    = print,
    pairs    = pairs,
    ipairs   = ipairs,
    tonumber = tonumber,
    tostring = tostring,
    -- intentionally omitted: os, io, require, dofile, loadfile, load
}

local chunk = assert(loadfile("untrusted.lua", "t", env))
chunk()

Any reference to os.execute, io.open, or require inside untrusted.lua resolves through _ENV and hits a nil, which raises an “attempt to call a nil value” error. This is the standard pattern for plugin systems, mod loaders, and user-supplied scripts.

Syntax-check a file without running it

-- lint.lua
local chunk, err = loadfile("script.lua")
if not chunk then
    io.stderr:write("syntax error: " .. err .. "\n")
    os.exit(1)
end
print("OK: script.lua parses")

Tools that validate Lua code (linters, formatters, build hooks) use this shape: load the file, look at the result, never call the function. Side effects in the file are not triggered.

loadfile vs dofile

dofile([filename]) is roughly equivalent to:

local function dofile(name)
    local chunk, err = loadfile(name)
    if not chunk then error(err) end
    return chunk()
end

The behavioral difference: dofile propagates any error to its caller; loadfile hands it back to you. Reach for loadfile when you want to handle the failure locally, defer execution, or swap the chunk’s environment. Reach for dofile when you just want to run a file and let errors bubble.

Notes and Gotchas

  • Forgetting to call the returned function is the most common bug. local f = loadfile("foo.lua") does not run foo.lua; you must call f().
  • There is no chunkname parameter. If you need a friendly name in error messages that differs from the path, use load directly: load(io.open(path, "rb"):read("*a"), "@" .. friendly_name).
  • Default mode is "bt", which accepts precompiled chunks. For anything that came from outside your codebase, pass mode = "t" to refuse binary input.
  • loadfile does not cache. Calling it twice on the same file parses the file twice and returns two distinct functions. Use require if you want a single shared instance per path.
  • File-not-found and syntax errors both come back as nil, errmsg. I/O errors start with "cannot " (e.g., cannot open foo.lua: No such file or directory); syntax errors look like foo.lua:LINE: .... Match the prefix if you need to tell them apart.
  • If the code is already a string, use load. Reaching for loadfile on a string forces a write-then-read round trip through disk, which is wasteful and adds another failure mode.

See Also

Related reference pages:

  • load: load a chunk from a string or function; has a chunkname parameter
  • dofile: load and execute a file in one call
  • require: cached module loader
  • assert: typical wrapper for loadfile’s nil, errmsg return
  • pcall / xpcall: protected call, useful when running a loaded chunk
  • error: raise an error after a load failure

Guides and tutorials: