Breakpoints and runtime inspection
debugSet conditional breakpoints to capture call stacks and in-scope variables or log Luau expression values. Read snapshots afterward to see exact values at the moment each breakpoint fired.
Instructions
Sets breakpoints that record the stack and variables when they are hit, then reads back what they caught.
These are tracepoints, not a step debugger. A breakpoint fires, captures the call stack and the variables in scope, and lets execution continue; op: "snapshots" returns what was captured. Studio's debugger has to decide whether to resume the instant it stops, and cannot wait for a tool call to come back with an answer, so stepping through code line by line is not possible this way — but 'what was this value when it got here' is, which is usually the actual question.
condition is a Luau expression evaluated where the breakpoint sits, so a breakpoint can fire only on the case that matters — health < 0, player.Name == "someone".
logMessage is ALSO a Luau expression, not a template string: its value is printed when the breakpoint is hit, so write "health=" .. health rather than health={health}. Prose is a syntax error. Read the lines back with console.
Two things about it are measured, not assumed, and both waste your time otherwise. A breakpoint fires ONCE PER RUN, not once per pass: on a five-iteration loop it printed a single line, for the first iteration only. It is not a way to watch a value change inside a loop — to see every pass, have the code itself print and read that with console. And a log expression CANNOT SEE THE LOOP CONTROL VARIABLE: on for index = 1, 5 do, a breakpoint in the body read the body's own locals correctly and index as nil. Wrap values in tostring so a nil prints as "nil" instead of throwing.
A log expression that throws is reported as "Breakpoint ... ignored" in console, NOT here — set still returns Verified, because Studio only compiles the expression once the line is reached.
So the two kinds cost different things: a logMessage breakpoint never stops and gives you one line you composed in advance, while one without it stops briefly and gives you the whole frame — every local and its type, without having to guess beforehand which value would matter. Both give you that for one pass only. Reach for the log when you know what to watch, the capture when you do not.
Only one breakpoint exists per line, so the same line cannot both log and capture.
Put the breakpoint on a line that does something. A return, an end or a bare declaration can verify and then never fire — measured, not guessed: the same breakpoint moved from return squared, tag to the assignment above it went from silent to firing on every pass. If one verifies but catches nothing, suspect the line before suspecting the condition.
Breakpoints belong to the session that holds them. Set them in the editor session BEFORE starting a playtest, since code that already ran cannot be caught retroactively.
Nothing here leaves a thread stopped waiting for you. A capture breakpoint stops for as long as it takes to read the frame and then resumes itself, so a script with one mid-loop still runs to its last line, and the user is never left with a frozen Studio to rescue.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| op | Yes | 'set' adds breakpoints, 'clear' removes one or all, 'snapshots' reads what has been captured, 'exceptions' controls breaking on errors. | |
| line | No | clear only: which line to remove. | |
| mode | No | exceptions only: break on every error, only unhandled ones, or never. Defaults to Unhandled. | |
| path | No | clear only: remove breakpoints from this script. Omit to clear everything. | |
| clear | No | snapshots only: discard what is returned, so the next read starts fresh. | |
| limit | No | snapshots only: how many of the most recent to return. | |
| studioId | No | Target Studio; omit for the active one. | |
| breakpoints | No | set only: breakpoints to add. |