Chapter 8: Debugging
When a script does not behave as expected, you have two main tools: compile-time error messages and the live trace. This chapter shows how to use both.
Compile errors
When you save a script with @ in the editor, the script is compiled
immediately. If there is a problem, you will see an error message with
a line number:
Script compilation error: line 3: unknown command 'gret'
Common compile errors:
| Error | Meaning | Fix |
|---|---|---|
| unknown command 'X' | Typo in a command name | Check spelling: greet vs gret |
| undefined variable '$X' | Using a variable that was never declared | Add a let, check spelling, or pass it as a parameter |
| expected block | A construct like if or each needs { } |
Add braces around the body |
| duplicate name 'X' | Two let or def with the same name in the same scope |
Rename one of them |
The error tells you which line has the problem. Use &r in the
editor to redisplay the script with line numbers, then &l N text to
fix line N.
Turning on tracing
Tracing shows you every instruction the script executes, in real time. To enable it:
- Start editing the mob:
olc medit <vnum> - Turn on tracing:
olc mtrace on - Stay in the room with the mob and trigger the event.
For objects and rooms:
olc oedit <vnum>
olc otrace on
olc rtrace on # traces the room you are currently in
Trace output appears in cyan to immortals in the same room as the script's owner. It looks like:
[Script Trace] --- after command [handler 1] ---
[Script Trace] stack[0]:
[Script Trace] PUSH_CONST "help"
[Script Trace] stack[1]: help
[Script Trace] LOAD_LOCAL $args (0,3)
[Script Trace] stack[2]: help, <iterable>
[Script Trace] CALL_ROUTINE keyword (2 args)
[Script Trace] stack[1]: help, true
[Script Trace] REQUIRE
...
This shows:
- Which handler is running (
after command [handler 1]). - The value stack after each instruction.
- Which builtins are being called and their results.
Reading trace output
The trace shows a stack-based execution. Values are pushed onto the stack, operations consume them and push results. Key things to look for:
REQUIRE— if the stack value isfalse, the handler stops and advances to the next. Iftrue, execution continues.CALL_ROUTINE— a builtin call. The result is pushed onto the stack.CALL_BLOCK— yourdeffunction or inline block is being called.DO_COMMAND— thedocommand is about to execute a game command.RUNTIME ERROR— the script hit a fatal error and stopped.
Turning off tracing
olc mtrace off
olc otrace off
olc rtrace off
Always turn tracing off when you are done — it generates a lot of output and can be distracting.
Common runtime errors
These appear in the trace output or as error messages:
| Error | Cause | Fix |
|---|---|---|
| type error: expected bool, got int | Passing an integer to if or require |
Use a comparison: [gt $n 0] not $n |
| type error: expected int | Arithmetic on a non-integer | Use [int $str] to convert a string |
| stale creature reference | The creature was removed from the world | The script silently stops; check with [exists $target] before use |
| too few arguments | Calling a block with fewer args than it declares | Check the block's <params> |
| execution limit exceeded | Infinite loop or very expensive computation | Simplify the logic; check loop bounds |
Debugging strategies
1. Check whether the handler fires at all
Add a simple do "say" as the first line:
after command (say) {
do "say DEBUG: handler fired"
require [keyword $args quest]
do "say Here is the quest info."
}
If you never see "DEBUG: handler fired", the filter is wrong or the event is not matching.
2. Check variable values
Interpolate variables into a do "say" to inspect them:
after command (say) {
do "say DEBUG: arg='$arg'"
let n [count $args]
do "say DEBUG: word count=$n"
}
3. Check require conditions separately
If a require seems to fail unexpectedly, test the condition in
isolation:
after command (say) {
let result [keyword $args quest]
do "say DEBUG: keyword result=$result"
require $result
do "say You asked about a quest!"
}
4. Check entity references
If a script stops silently mid-execution, a stale entity reference may
be the cause. Use exists to guard:
if [exists $target] {
do "say [name $target] is still here."
} else {
do "say They left."
}
The offline debugger
For testing scripts without a running MUD, you can use the command-line script debugger:
./bin/circle --script-debug file.script sample_lib [command] [args...]
This compiles and runs a script file against the sample library data, optionally simulating a game command. It prints the full trace to stdout.
Key points
- Compile errors appear when you save with
@. They include a line number. olc mtrace onenables live tracing for the mob you are editing. Alsootracefor objects,rtracefor rooms.- Trace output shows the instruction-by-instruction execution to immortals in the room.
- Turn tracing off when done:
olc mtrace off. - Debug with
do "say"— print variable values to see what the script is doing. - Check
existsbefore using entity references that might be stale.
Back to index