dofile: Open and Execute a Lua File
dofile ([filename]) dofile is one of Lua’s basic functions for loading and running code from the filesystem. It opens a file, compiles the contents as a Lua chunk, and executes that chunk. Whatever the chunk returns flows back to you, with the full return arity preserved.
The function is defined in §6.1 of the Lua 5.4 Reference Manual.
Signature
dofile ([filename])
Parameters
dofile takes a single optional argument.
| # | Parameter | Type | Default | Description |
|---|---|---|---|---|
| 1 | filename | string? | standard input (stdin) | Path to the file to load and execute. Resolved against the process’s current working directory. Not searched against package.path or LUA_PATH. |
The argument is a raw OS path. Forward slashes work on every platform; backslashes work on Windows inside double-quoted string literals. If you want module-style path resolution, use require instead.
Return Value
dofile returns all values the chunk returns, in order, with full arity:
- A chunk ending in
return a, b, creturns three values. - A chunk ending in a trailing expression like
1, 2returns two values (a trailing expression is also a return value). - A chunk with no
returnat all returns nothing, anddofilethen returns nothing.
A chunk with no trailing return is treated as a script. The values flow back through dofile as either a multi-value or as nothing at all, depending on the last expression in the file:
-- greet.lua
print("loaded")
Assigning the result to a single local gives nil, because there is nothing to assign. The script’s side effect (the print) still runs, but no values are produced:
local result = dofile("greet.lua")
print(result) -- nil
If the file cannot be opened, the chunk has a syntax error, or the chunk raises a runtime error, dofile does not catch it. The error propagates to the caller, unchanged.
Loading a config file
The most common use of dofile is loading a Lua file as configuration. A .lua file is just a chunk, so it can return any value, usually a table.
config.lua returns a table with three connection settings. The trailing return is what makes the file act as a value rather than a script:
return {
host = "127.0.0.1",
port = 8080,
debug = true,
}
Calling dofile on the file path assigns the returned table to a local variable named cfg. You can then read any field directly off cfg like any other Lua table:
local cfg = dofile("config.lua")
print(cfg.host, cfg.port, cfg.debug)
Running that loader produces three tab-separated values on a single line, matching the three fields the config table returned. The first two are a string and a number, the third a boolean, exactly as defined in config.lua:
127.0.0.1 8080 true
Most ~/.luarc-style dotfiles and game config screens follow this pattern. Because dofile re-reads the file on every call, you get hot-reloading for free if you call it again at runtime.
Capturing multiple return values
dofile preserves the chunk’s full return list, which makes it a clean way to expose a small set of values or functions from a script.
mathx.lua defines two local helper functions and then returns both of them. The trailing return produces two values, which is the main point of this example:
local function add(a, b) return a + b end
local function mul(a, b) return a * b end
return add, mul
The caller assigns the chunk’s multiple return values to two local variables, one per value. This positional destructuring is what makes dofile a clean way to expose a small API from a file:
local add, mul = dofile("mathx.lua")
print(add(2, 3), mul(4, 5))
The print statement shows the two computed results on a single line, separated by a tab. The first call to add(2, 3) returns 5, and mul(4, 5) returns 20, exactly as the chunk produced:
5 20
You can return as many values as you need. The caller can use select("#", ...) to count them, or just destructure positionally.
Error handling: wrap with pcall
dofile does not run in protected mode. A missing file, a syntax error, or a runtime error inside the chunk all crash the caller. Wrap the call in pcall if the file is optional.
broken.lua raises an error on its first line and does nothing else. The error call is the simplest possible runtime failure inside a chunk:
error("intentional failure")
Wrapping dofile in pcall converts the propagated error into a regular return value. The first return is a boolean indicating success, and the second carries the error message:
local ok, err = pcall(dofile, "broken.lua")
print(ok, err)
When run, this prints false followed by the error message, prefixed with the file path. The path prefix is .../broken.lua:1:, which tells you both where the file was and which line raised the error:
false .../broken.lua:1: intentional failure
What makes the error message especially useful is the file path prefix, which is far more informative than a bare error string. For traceback control, swap pcall for xpcall with a custom message handler.
dofile vs loadfile, load, and require
Lua has four ways to load and run code, and they differ in subtle but important ways.
| Function | Reads from | Returns | Caches | Path search | Protected mode |
|---|---|---|---|---|---|
dofile | a file (or stdin) | runs the chunk | no | no | no |
loadfile | a file (or stdin) | the chunk function, or nil + error | no | no | yes |
load | a string or function | the chunk function, or nil + error | no | n/a | yes |
require | a file via package.path / package.cpath | the chunk’s return values, cached | yes (package.loaded) | yes | yes (re-raises) |
A few practical takeaways:
- Reach for
requirefor modules. Caching,package.pathsearch, and the single-load guarantee save you from reinventing them. - Use
loadwhen the code is already in memory: a network response, a string built at runtime, a function that yields chunks. - Use
dofilewhen you genuinely want a side-effecting, re-running, no-search chunk: config files, REPL input, a script you trust. - Use
loadfilewhen you wantdofile’s file semantics but need to inspect the chunk before running it, or want protected-mode error reporting asnil+ error instead of an exception.
dofile is roughly loadfile plus an immediate call, minus the protected-mode reporting. Compare the two side by side:
-- dofile: runs the chunk, propagates errors as exceptions
local result = dofile("config.lua")
-- loadfile: returns a chunk function, or nil + error if compilation fails
local chunk, err = loadfile("config.lua")
if not chunk then
print("could not load:", err)
else
local result = chunk() -- you run it yourself, when you want
end
The loadfile form is what you want when you need to inspect the chunk, defer execution, or report load errors as values instead of exceptions. dofile is the shorter form for the case where you just want to run the file.
The full module-loading story is in the Modules and require tutorial.
Common Pitfalls
A handful of mistakes show up over and over:
-
Forgetting
pcall. A missing config file is a runtime crash. Wrap withpcallif the file is optional, or pre-check withio.openbefore callingdofile:local cfg local f = io.open("config.lua", "r") if not f then cfg = defaults else f:close() cfg = dofile("config.lua") end -
Assuming
package.pathapplies. It doesn’t.dofile("foo.lua")looks forfoo.luain the current working directory, full stop. There is noLUA_PATHlookup and nopackage.searchersinvolvement. The next snippet fails unlessfoo.luais literally in the cwd, even ifpackage.pathis set to find it:-- works only if "./foo.lua" exists relative to cwd local lib = dofile("foo.lua") -- use require if you want package.path search local lib = require("foo") -
Caching expectations. Re-calling
dofile("x.lua")re-runs the file. Globals it sets or mutates happen every time. With the samesetup.luaon disk, the first call printssetup ranonce withrequireand twice withdofile:-- setup.lua: print("setup ran") dofile("setup.lua") -- prints "setup ran" dofile("setup.lua") -- prints "setup ran" again require("setup") -- prints "setup ran" require("setup") -- no-op, cached -
stdinsurprise.dofile()with no argument reads fromstdin. That’s useful for piping. The next line reads Lua code from a pipe and runs it throughdofile():echo 'print(1+1)' | lua -e 'dofile()'In a non-interactive host (a GUI app, a server, anything without a tty),
stdinusually blocks or returns nothing on EOF, so the call hangs or silently does nothing. -
Wrong working directory.
dofileis resolved against the process’s current working directory, not the script’s location. If your tool changescwdmid-run, relative paths break. The chunk can recover by reading its own path fromarg[0]and building sibling paths from that:-- inside a chunk, derive its own directory and load a sibling file local script = arg[0] or "" local here = script:match("(.*/)") or "./" local helpers = dofile(here .. "helpers.lua")
Version Notes
The signature dofile ([filename]) and its semantics (file read, chunk compiled, chunk run, errors propagated, no caching) have been stable from Lua 5.1 through Lua 5.4. The 5.2 changes to load and loadfile did not touch dofile. The 5.4 manual still describes the same behavior, and the implementation in lbaselib.c is essentially unchanged.
If you maintain code that has to run on 5.0 or earlier, the no-argument dofile() reading from stdin is documented the same way in every edition.
See Also
load- load a chunk from a string or function.require- module loading withpackage.pathsearch and caching.pcallandxpcall- protected-mode wrappers fordofile.- Modules and
require- the module-loading story in depth. - Config files in Lua - config-file patterns and gotchas.