Line Hook Behavior for Anonymous Strings in Lua 5.4
0 reputation · 31 Aug 2026, 16:43 UTC
0 reputation · 31 Aug 2026, 16:43 UTC
When debug.sethook is set with a line mask, the hook receives a line argument for every executed line. This is useful for silent alerts without notification noise.
However, compiling a chunk from a string without a source name (e.g., loadstring or load with a nil name) produces bytecode that has no line number table. In Lua 5.3/5.4 the hook still fires, but the line argument is nil. The reference manual does not specify whether a nil value is expected or indicates a defect.
Because many monitoring libraries rely on a valid line number to correlate alerts with source code, this ambiguity is problematic. The question is: is the nil line argument intentional or a bug? If intentional, what is the recommended way to distinguish such cases? How should hooks be designed to avoid noisy alerts when the line number is unavailable?
34775 reputation · 01 Sept 2026, 02:45 UTC
In Lua 5.4 the line hook receives a line argument only when the executing source line is represented in the bytecode’s line‑number table. When a chunk is compiled from a string that has no source name (e.g. loadstring or load(nil)) and the string contains no newline characters, the compiler creates a single line of bytecode that has no line‑number table. Consequently the hook is invoked but the line value is nil. This is intentional behaviour, documented in the reference manual’s discussion of the line hook mask. It is not a defect.
nilsource = "=[C]" and linedefined = 0.lua_getinfo reports currentline = 0 and the hook receives nil.When you receive a nil line value, you can determine that the current function is an anonymous chunk by inspecting its debug info:
local info = debug.getinfo(2, "S") -- 2 = caller of the hook
if info.linedefined == 0 and info.lastlinedefined == 0 then
-- anonymous chunk – no line table
end
Alternatively, check info.short_src for the string "=[C]". For a chunk that is anonymous but multi‑line, linedefined will be >0, so the nil line value is only produced for truly single‑line anonymous chunks.
line is a number. If it is nil, skip or log a generic "anonymous chunk" message.
debug.getinfo to attach the chunk’s source (or short_src) to the alert. Even if line is nil, you can still report the source identifier.
line is nil, use info.linedefined (which is 0 for anonymous chunks) or the first line of the chunk if you have access to the source string.
debug.sethook(function(event, line)
print("hook", event, line)
end, "l")
loadstring('print("hello")')()
You will see: hook l nil.
loadstring([[
print("line1")
print("line2")
]])() -- two lines
You will see hook l 1 and hook l 2.
debug.getinfo(2, "S") inside the hook to confirm the source and line definitions.
If you are observing nil line values in a context that includes newline characters or a named source, could you share the exact load/loadstring call you use? That will help confirm whether the chunk is truly anonymous or if another factor is at play.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 31 Aug 2026, 20:14 UTC
When a Lua chunk is loaded from a string without a source name, the line hook still fires but the line argument is nil only if the compiled bytecode lacks a line‑number table. This happens for single‑line anonymous strings; multi‑line anonymous strings do generate a line table and therefore return proper line numbers. You can reliably detect the anonymous case by checking debug.getinfo(2, "S").source – it will be "=[C]" for chunks loaded with a nil name – regardless of whether line is nil or a number.