Debug

Inspect and modify function constants, prototypes, upvalues and stack values.

21 functionssUNC 100%Lynx 0.0.0

The module extends debug with prototype, closure and frame readers. Indices for constants, child prototypes, upvalues and stack slots are 1-based. Stack levels refer to frames, not lexical local variable names. Nil values create holes in snapshot tables; use pairs when inspecting them.

Prototype readers accept a Luau function or a numeric stack level resolving to a Luau frame. C targets and unavailable frames raise errors. debug.getinfo also understands the module visible-stack mapping and original-thread context. The native runtime debug table is merged afterward, retaining client-provided functions such as debug.info and debug.traceback.

debug.getregistry

debug.getregistry(): table

Returns the calling VM registry table.

Returns

table

Same table as getreg().

Example

print(rawequal(debug.getregistry(), getreg()))

debug.getmetatable

debug.getmetatable(value: any): table?

Returns the actual metatable, ignoring its __metatable visibility field.

Parameters

valueany
Value whose actual metatable is requested.

Returns

table?

Raw metatable or nil.

Example

print(debug.getmetatable(setmetatable({}, {})))

debug.setmetatable

debug.setmetatable(value: any, metatable: table?): table?

Calls the native metatable setter directly. Unlike setrawmetatable, it does not temporarily clear a table readonly flag. With the normal two-argument call, it returns the supplied metatable, not the original value or a success boolean.

Parameters

valueany
Value to update.
metatabletable?
New table or nil.

Returns

table?

Supplied metatable for a two-argument call.

Example

local t, mt = {}, {}
print(debug.setmetatable(t, mt) == mt)

debug.getconstant

debug.getconstant(target: function | number, index: number): any

Reads one raw constant-pool position. Function/table constants return nil. Invalid indices raise an error; this API has no compact-index argument.

Also available as getconstant.

Parameters

targetfunction | number
Luau function or stack level.
indexnumber
Positive raw constant index.

Returns

any

Constant value, or nil for a hidden/non-value position.

Example

local f = function() return "sample", 17 end
for index, value in pairs(debug.getconstants(f)) do
print(index, debug.getconstant(f, index))
end

debug.getconstants

debug.getconstants(target: function | number, pseudoIndices: boolean? = false): {any}

Returns the constant pool. By default numeric keys retain raw positions and unsupported function/table entries become holes. With pseudoIndices true, those entries are skipped and the visible positions are compacted.

Also available as getconstants.

Parameters

targetfunction | number
Luau function or stack level.
pseudoIndicesboolean?
Optional.Compact past function/table constant positions. Default false.

Returns

{any}

Constant snapshot keyed by raw or compacted positions.

Example

for index, value in pairs(debug.getconstants(function() return "abc" end)) do
print(index, value)
end

debug.setconstant

debug.setconstant(target: function | number, index: number, value: any, pseudoIndices: boolean? = false): ()

Replaces a constant while preserving its original TValue type. Supports nil, boolean, number, vector and string values; unsupported constants, mismatched types and invalid positions raise errors.

Also available as setconstant.

Parameters

targetfunction | number
Luau function or stack level.
indexnumber
Positive constant position.
valueany
Same-type replacement constant.
pseudoIndicesboolean?
Optional.Interpret index using the compact visible pool. Default false.

Returns

()

No values.

Example

local f = function() return "before" end
for i, value in pairs(debug.getconstants(f)) do
if value == "before" then debug.setconstant(f, i, "after") end
end
print(f())

debug.getupvalue

debug.getupvalue(target: function | number, index: number): any

Reads one upvalue through the VM API. A direct function may be C or Luau. Missing/invalid upvalues raise errors; the result is the value only, not a name/value pair.

Also available as getupvalue.

Parameters

targetfunction | number
Function or nonnegative stack level.
indexnumber
Positive upvalue index.

Returns

any

Selected upvalue value.

Example

local captured = {}
local function f() return captured end
print(debug.getupvalue(f, 1) == captured)

debug.getupvalues

debug.getupvalues(target: function | number): {any}

Reads all upvalues of a Luau closure, resolving open and closed captured cells. C functions raise Unsupported function.

Also available as getupvalues.

Parameters

targetfunction | number
Luau function or nonnegative stack level.

Returns

{any}

Snapshot indexed by upvalue position; nil entries become holes.

Example

local captured = {}
local function f() return captured end
for i, v in pairs(debug.getupvalues(f)) do print(i, v) end

debug.setupvalue

debug.setupvalue(target: function | number, index: number, value: any): ()

Writes one Luau closure upvalue through the native setter. C functions and invalid indices raise errors.

Also available as setupvalue.

Parameters

targetfunction | number
Luau function or nonnegative stack level.
indexnumber
Positive upvalue index.
valueany
Replacement value, including nil.

Returns

()

No values.

Example

local box = {}
local function f() return box end
local replacement = {}
debug.setupvalue(f, 1, replacement)
print(f() == replacement)

debug.getproto

debug.getproto(target: function | number, index: number, active: boolean? = false): function | {function}

Reads one direct child prototype. The default result is an inspection closure constructed from a cloned prototype with a return-only body; it is not the original child executable body.

With active true, scans GC for existing live Luau closures using the exact child prototype and returns an array of those closures.

Also available as getproto.

Parameters

targetfunction | number
Luau parent closure or stack level.
indexnumber
Positive direct-child prototype position.
activeboolean?
Optional.Return live existing child closures instead of an inspection closure. Default false.

Returns

function | {function}

Inspection closure, or matching active closure array.

Example

local function parent()
return function() return "child" end
end
local child = parent()
for _, fn in ipairs(debug.getproto(parent, 1, true)) do print(fn == child) end

debug.getprotos

debug.getprotos(target: function | number): {function}

Builds inspection closures for each direct child prototype. Their cloned bodies return immediately; use getproto(target, index, true) when actual live executable child closures are needed.

Also available as getprotos.

Parameters

targetfunction | number
Luau parent closure or stack level.

Returns

{function}

One inspection closure per direct child prototype.

Example

local function parent() return function() end end
for i, fn in ipairs(debug.getprotos(parent)) do print(i, debug.getinfo(fn)) end

debug.getstack

debug.getstack(level: number, index: number? = nil): any | {any}

Reads a raw register slot from a live Luau frame, or returns all frame slots when index is omitted. Non-dead values are copied; a dead collectible slot becomes nil. C frames and invalid levels/positions raise errors.

Also available as getstack.

Parameters

levelnumber
Nonnegative stack level resolving to a live Luau frame.
indexnumber?
Optional.Optional positive register-slot index. Default nil.

Returns

any | {any}

One register value or snapshot of all allocated frame slots.

Example

local function inspect()
local marker = {}
for i, value in pairs(debug.getstack(1)) do print(i, value == marker) end
end
inspect()

debug.setstack

debug.setstack(level: number, index: number, value: any? = nil): ()

Writes a raw register slot in a live Luau frame. Omitted/nil value clears that slot. Slots are VM registers, not stable source-local names; invalid frames and positions raise errors.

Also available as setstack.

Parameters

levelnumber
Nonnegative live Luau frame level.
indexnumber
Positive register-slot index.
valueany?
Optional.Replacement slot value. Default nil.

Returns

()

No values.

Example

local function inspect()
local before = debug.getstack(1, 1)
debug.setstack(1, 1, before) -- preserve the existing slot
end
inspect()

debug.getinfo

debug.getinfo(target: function | number, options: string? = "sluanf"): table

Builds an information table for a function or positive visible stack level. Requested fields are selected by the option letters below. Missing stack levels raise an error.

Also available as getinfo.

Parameters

targetfunction | number
Function object or positive visible stack level.
optionsstring?
Optional.Requested option characters. Default "sluanf".

Returns

table

Only the requested information fields.

Example

local info = debug.getinfo(function(a, ...) return a end)
print(info.source, info.numparams, info.is_vararg)
OptionReturned fields
ssource, short_src, what, linedefined
lcurrentline
unups
anumparams, is_vararg (numeric flag in getinfo)
nname
ffunc

debug.setinfo

debug.setinfo(target: function | number, information: table): ()

Records optional debug-display overrides for name, source, short_src and currentline. Omitted fields remain unset in the replacement override record. It does not rewrite compiled code or line tables.

Parameters

targetfunction | number
Function or nonnegative stack level resolving to Luau.
informationtable
Optional name/source/short_src strings and currentline number.

Returns

()

No values.

Example

local function f() end
debug.setinfo(f, { name = "example", source = "=example" })
print(debug.getinfo(f).name)

debug.getcallstack

debug.getcallstack(thread: thread? = nil): {table}

Collects frame entries for the current/original observed thread, or an explicitly supplied thread. Each entry contains func, name and source; Luau entries also contain currentline.

Also available as getcallstack.

Parameters

threadthread?
Optional.Optional explicit thread to inspect. Default nil.

Returns

{table}

Innermost-first frame entries; explicitly inspected foreign threads include level 0.

Example

for i, frame in ipairs(debug.getcallstack()) do
print(i, frame.name, frame.source, frame.currentline)
end

debug.isvalidlevel

debug.isvalidlevel(level: number, thread: thread? = nil): boolean

Checks for a function at a positive stack level. Nonpositive levels return false. An explicitly inspected finished or errored coroutine also returns false; noninteger levels raise an argument error.

Also available as isvalidlevel, validlevel and debug.validlevel.

Parameters

levelnumber
Integer stack level.
threadthread?
Optional.Thread to inspect; nil selects caller. Default nil.

Returns

boolean

Whether the frame currently resolves to a function.

Example

print(debug.isvalidlevel(1))
print(validlevel(-1))

getcurrentline

getcurrentline(level: number? = 1): number

Returns the current source line of a positive stack level. Missing frames or frames without line information raise an error.

Also available as debug.getcurrentline.

Parameters

levelnumber?
Optional.Positive stack level. Default 1.

Returns

number

Source line number.

Example

print(getcurrentline())

Native runtime debug functions

The module copies the main runtime debug table into its extended table. These built-in names are supplied by the runtime rather than registered as aliases of the module getinfo function; additional client-specific fields are retained as provided by that runtime.

debug.info

debug.info([thread: thread,] target: function | number, options: string): ...any

Returns positional information in option-string order. s, l, n, and f select source, line, name and function; a returns parameter count followed by a boolean vararg flag. Duplicate/unknown option characters raise an error.

Parameters

threadthread?
Optional.Optional leading thread for stack inspection. Default nil.
targetfunction | number
Function object or stack level.
optionsstring
Requested option sequence.

Returns

...any

Requested positional results; an unavailable stack frame returns no values.

Example

local source, line = debug.info(1, "sl")
print(source, line)

debug.traceback

debug.traceback([thread: thread,] message: string? = nil, level: number? = 1): string

Formats a runtime stack traceback, optionally prepending a message. The default start level is 1 for the current thread and 0 when inspecting another thread. Negative levels raise an error.

Parameters

threadthread?
Optional.Optional leading thread to inspect. Default nil.
messagestring?
Optional.Optional message prefix. Default nil.
levelnumber?
Optional.First nonnegative stack level; defaults to 0 for another thread. Default 1.

Returns

string

Formatted stack trace.

Example

print(debug.traceback("Trace:", 1))

error

error(value: any, level: number? = 1): never

Raises the supplied error value. For string errors with a positive level, the module remaps the location through visible frames so hidden forwarding frames do not shift the reported caller. Nonstring values and level 0 retain the native behavior.

Parameters

valueany
Error object or message.
levelnumber?
Optional.Location-reporting level; 0 omits the source location. Default 1.

Returns

never

The call raises an error and does not return normally.

Example

local ok, reason = pcall(function()
error("example", 0)
end)
print(ok, reason)