The ultimate guide to debugging

Debugging is a useful skill in programming that will help you find bugs much quicker that could otherwise be hiding in your code.

Everyone writes bugs, even the best programmers. The best programmers know how to find them, so lets go over how to catch the most sneaky bugs in Roblox.

Lets assume you have some code

local function discombobulator(t)
  t.cat = 5
  if t.x == 10 then
    t.x = 9
  end

  if t.x == 10 then
    print("boop")
  end
end

The bug should be obvious straight away, but imagine this bug is in a much bigger codebase, this could be hiding away and you’d never spot it, so lets give some ways of catching this

The stack trace

Before we go into methods of debugging, we should use the information Lua gives to us when an error occurs, this is known as the stack trace, it provides a useful call stack for finding the exact location of a bug, and what functions are calling the stack

This code:

local function c()
	error("NOOOOOOO")
end

local function b()
	c()
end

local function a()
	b()
end

a()

… will give us this stack trace

  Workspace.Script:2: NOOOOOOO - Script:2
  Stack Begin
  Script 'Workspace.Script', Line 2 - function c - Script:2
  Script 'Workspace.Script', Line 6 - function b - Script:6
  Script 'Workspace.Script', Line 10 - function a - Script:10
  Script 'Workspace.Script', Line 13 - Script:13
  Stack End

As you can see, the function c is throwing the error, but we can also look into what functions were called that results in c throwing the error. In this exact case, the c function itself is throwing the error regardless of what it gets, but lets change the code to this

local function b(t)
	t.didDoSomething = true
end

local function a()
	b {}
end

local function c()
	b "cat"
end

a()
c()

The stack trace is now

  Workspace.Script:2: attempt to index string with 'didDoSomething' - Script:2
  Stack Begin
  Script 'Workspace.Script', Line 2 - function b - Script:2
  Script 'Workspace.Script', Line 10 - function c - Script:10
  Script 'Workspace.Script', Line 14 - Script:14
  Stack End

The stack trace will let us see that the error occured indirectly through the function c, while the function a worked as intended.

The caveman method

Using print will allow you to catch 99% of bugs if you can output the info that you need at that exact location, Roblox pretty prints table, so if we add a print statement after the first if statement, we can spot that x is somehow being set to 9

print(t) --> {x = 9}

The reason I call this the caveman method is because its very primitive, and there are a few bugs where this wont be that useful for those bugs in specific (ie coroutines or objects that are somehow being set to nil when it shouldn’t be)

The Roblox debugger

Roblox Studio ships with an insanely powerful debugger, you can quickly stop a script on a line by adding a breakpoint

You cant use breakpoints inside callbacks that cant yield. the Roblox documentation should already tell you what callbacks cant yield, for example:

DockWidget:BindToClose(function()
  print("Hello World") --> Cannot yield
end)

You can do this by clicking a line of code:
image

If we now run the code, Roblox will stop the game and start the debugger, the debugger will now give us a few buttons to control how we step through this code

image

  • Step Into: Perform the next line of code
  • Step Over: Perform the next line of code, but do not look into the function being called
  • Step Out: Escape the current function if inside one

But these buttons aren’t very powerful on their own since it just lets us step over code, if we cant get anything out of this, whats the point of it

This is where the Watch comes in (or you could just put prints everywhere, lol)

WATCH TODO (the new watch window is still a little janky to me)

There’s a few other cool things you can do with breakpoints, by right-clicking it and pressing Edit Breakpoint;

Log Message

This outputs a message when the breakpoint is hit, this is the same as putting a print directly after the breakpoint

Conditional Breakpoint

The breakpoint will only stop if a condition is met, this is a Luau expression based on the current variables in scope, I have not found a good usecase for this, however I imagine it is;

8 Likes
Hidden Quote

A lot of the Stuff here was already shown with Documentation

Its a Good Tutorial, Its just It may not be nessacary due to there already being plenty of sources, but Useful neitherless.

1 Like