Guide · Debugging

How to fix Roblox script errors

When a Roblox script breaks, Studio usually says where and why in the Output window. This guide shows how to read that message, what the common errors mean and how to fix each one, what to check when a script fails without any error, and a routine that finds the cause instead of guessing.

Updated 30 September 202619 min readBeginnerLuau

In short

Open the Output window from the Window menu or the Script tab, press Play, and click the first red error. Studio opens the script at the failing line. The message says what went wrong there: attempt to index nil with 'Humanoid' means the value just before .Humanoid was nil. Fix the reason that value is missing, then play again.

  • Start with the first error in the list. Errors further down are often knock-on effects of it.
  • The common runtime errors come down to three causes: a value that is nil, a name that is misspelt or has not loaded yet, or code running on the wrong side (server or client).
  • No error at all usually means the code never ran. Check the script type, where it sits in the Explorer, its Enabled property, and whether the event it waits for ever fires.
  • To see what a value really is on a given line, print it with a label or pause on that line with a breakpoint.
On this page
  1. Read the Output
  2. Debugging routine
  3. Common errors
  4. No errors, nothing happens
  5. Debugging tools
  6. With RoCode
  7. Questions
  8. Sources

Open Output and read an error

The Output window is where Roblox reports script errors, engine warnings and anything your code prints. It is not always open, so open it before you test.

  1. Open Output from Studio's Window menu, or with its button on the Script tab.
  2. Clear old messages with Ctrl+K (⌘K on Mac) so you only see this test.
  3. Press Play (F5) and do the thing that breaks.
  4. Look for the first red line. Errors are red. Warnings, such as Infinite yield possible, are shown in orange.

A runtime error in Output looks like this. Depending on Output's display options, each line can also carry a timestamp, a Client or Server label, and the script name.

Output (example)
ServerScriptService.CoinPad:14: attempt to index nil with 'Coins'
Stack Begin
Script 'ServerScriptService.CoinPad', Line 14 - function award
Script 'ServerScriptService.CoinPad', Line 27
Stack End
  • ServerScriptService.CoinPad is where the script is in the Explorer: a script named CoinPad inside ServerScriptService.
  • :14 is the line that failed.
  • The message after the line number says what went wrong. The list of common errors below explains each one.
  • The stack, between Stack Begin and Stack End, lists the calls that led there, most recent first: line 14 inside the function award, which was called from line 27.

Click the red error and Studio opens that script with the cursor on the failing line. In a solo test, messages from the server and from your own client share one Output, labelled green for server and blue for client. The filters at the top narrow it by message type (Error, Warning), by context (Client, Server) or by text.

A debugging routine that works for any script

Guessing, and changing several things at once, is the slow way to fix a script. This order narrows the problem down step by step, whether or not there is an error message.

  1. Reproduce it. Clear Output, press Play, and do exactly what breaks it. Note what you expected and what happened instead.
  2. Take the first error. Read the first new red line: the script path, the line number and the message. If there is no error, go to scripts that run with no errors.
  3. Go to the line. Click the error. Read the whole line and ask which value on it could be nil, misspelt, or of the wrong type.
  4. Check the value instead of assuming it. Add a print just above the line, or set a breakpoint on it and read the values in the Watch window. typeof(x) tells you whether you have a number, a string, an Instance or nil.
  5. Follow it back. If the value is wrong, find where it was set: a FindFirstChild that found nothing, a function that returned nothing, an argument the caller never sent.
  6. Fix the cause, one change at a time. Correct the name, wait for the object, or move the code to the right side, then play again. Do not wrap the line in pcall to silence it: that hides the bug instead of fixing it.
  7. Confirm. Repeat step 1. The error should be gone and the feature should do what you expected. For anything involving other players, also test with Server & Clients and two clients.
  8. Clean up. Remove or switch off your debug prints so the next problem is easy to spot.

Common Roblox script errors and how to fix them

Each message follows a fixed pattern with your own names filled in, so the wording itself is the best clue. The quoted names below, such as 'Health' or "Workspace", are examples.

Common Output errors. Select a message for the full explanation.
Output saysWhat it meansUsual fix
attempt to index nil with 'Health'The value before .Health was nil.Find out why it is nil, and check it with if before using it.
Infinite yield possible on 'Players.YourName:WaitForChild("Character")'WaitForChild has waited 5 seconds and the object has not appeared.Fix the name or path, make sure something creates the object, or add a time-out.
Bridge is not a valid member of Workspace "Workspace"No child or property with that exact name exists there right now.Match the spelling and capitals in the Explorer. On the client, use WaitForChild.
attempt to call a nil valueYou called something that is not a function.Check the function name, and that it is defined above the call.
attempt to perform arithmetic (add) on Instance and numberMaths on something that is not a number.Use .Value on value objects, and tonumber on text.
attempt to concatenate string with nil.. tried to join text with nil or an object.Use tostring(), .Name, or string interpolation.
Expected 'end' (to close 'function' at line 3), got <eof>A syntax error. None of the script runs.Fix the red underline in the Script Editor.
Requested module experienced an error while loadingThe ModuleScript itself errored while it ran.Fix the module's own error, shown just above in Output.
Argument 1 missing or nilA Roblox function got nil where it needs a value.Trace where that argument should have come from.
FireServer can only be called from the clientA RemoteEvent was fired from the wrong side.The server uses FireClient; LocalScripts use FireServer.
Data store error 403, StudioAccessToApisNotAllowedStudio is not allowed to use data stores for this game.Publish, then turn on Studio Access to API Services.

attempt to index nil with 'X'

Indexing means reading something with a dot or brackets: part.Position, player.Character, list[1]. This error says the thing on the left was nil, and the quoted name is what you tried to read from it. So on the line player.Character.Humanoid, attempt to index nil with 'Humanoid' means player.Character was nil. When the key is a number, the message ends with number instead.

The usual reasons the value was nil:

  • FindFirstChild, FindFirstChildOfClass or FindFirstAncestor found nothing, because the name is different or the object does not exist yet.
  • Players.LocalPlayer was used in a server Script. It only exists in LocalScripts and the ModuleScripts they require; on the server it is always nil. Use the player that PlayerAdded or a RemoteEvent handler gives you instead.
  • player.Character was read before the character spawned, or while it was respawning.
  • A table entry was never set, such as playerData[player] before that player's data loaded, or a function you called returned nothing.

A kill brick is the classic case. The broken version assumes that whatever touches the part belongs to a character, which is not true for dropped tools, loose parts or accessories.

Script in a Part in Workspace named KillBrick
local part = script.Parent :: BasePart

part.Touched:Connect(function(hit: BasePart)
	-- Broken version:
	--   hit.Parent:FindFirstChild("Humanoid").Health = 0
	-- When the touching part is not a body part of a character, FindFirstChild
	-- returns nil and Output shows:
	--   attempt to index nil with 'Health'

	-- Fixed: check each step before using it
	local model = hit:FindFirstAncestorOfClass("Model")
	local humanoid = model and model:FindFirstChildOfClass("Humanoid")
	if humanoid then
		humanoid.Health = 0
	end
end)

Writing hit.Parent.Humanoid instead fails with a different message, Humanoid is not a valid member of ..., covered below. FindFirstAncestorOfClass("Model") also finds the character when the touching part is inside an accessory or a tool the character is holding, so a held tool touching the brick counts too.

The pattern that fixes this error: get the value, check it, and only then use it. On the server, FindFirstChild plus an if check suits things that may legitimately be missing. In a LocalScript, use WaitForChild for things the server creates, because they can arrive after your script starts.

Infinite yield possible on ...

WaitForChild("Name") pauses the script until a child with that name exists. If it has waited more than 5 seconds and you gave no time-out, Roblox prints this warning. It is a warning, not an error: the script is still waiting, and nothing after that line runs until the child appears, which may be never. The usual causes:

  • The name or the parent is wrong. WaitForChild("leaderStats") never matches a folder named leaderstats.
  • Nothing creates the object: the server script that makes it is disabled, in the wrong place, or errored first. Look for an earlier error in Output.
  • A LocalScript is waiting for something in ServerStorage or ServerScriptService. Their contents are never sent to players, so the wait never ends.
  • You are waiting for a property, not a child. player:WaitForChild("Character") is the common one: Character is a property of Player, not an object inside it.
  • Instance streaming (Workspace.StreamingEnabled) is on, and the part is too far from the player to have been sent to that client yet.
LocalScript in StarterPlayerScripts named DeathMessage
local Players = game:GetService("Players")

local player = Players.LocalPlayer

-- Broken version:
--   local character = player:WaitForChild("Character")
-- Character is a property of Player, not a child, so Output warns:
--   Infinite yield possible on 'Players.YourName:WaitForChild("Character")'

local function onCharacterAdded(character: Model)
	-- With a time-out, WaitForChild returns nil instead of waiting forever
	local humanoid = character:WaitForChild("Humanoid", 10)
	if not (humanoid and humanoid:IsA("Humanoid")) then
		warn(`[DeathMessage] no Humanoid in {character:GetFullName()} after 10 seconds`)
		return
	end
	humanoid.Died:Connect(function()
		print(`[DeathMessage] {player.Name} died`)
	end)
end

-- Handle the character that may already exist, then every respawn
local current = player.Character
if current then
	onCharacterAdded(current)
end
player.CharacterAdded:Connect(onCharacterAdded)

A number as the second argument of WaitForChild is a time-out in seconds. The script can then report the problem with warn and stop cleanly, instead of hanging silently.

X is not a valid member of Y

You used a dot to reach a child or property that does not exist at that moment, such as workspace.Bridge when Workspace has no part named Bridge. The message names what you asked for, then the class and full path of the object it looked in. Check three things:

  • Spelling and capitals match the Explorer exactly. The folder is leaderstats, all lowercase, and humanoid is not Humanoid.
  • Property names use American spelling. part.Colour fails with Colour is not a valid member of Part "Workspace.Part"; the property is Color.
  • The object has loaded. On the client, objects the server creates can arrive after your LocalScript starts, so use WaitForChild there.
LocalScript in StarterPlayerScripts named CoinsWatcher
local Players = game:GetService("Players")

local player = Players.LocalPlayer

-- Broken version:
--   local coins = player.leaderstats.Coins
-- The server creates leaderstats, and it can reach this client after this
-- script starts. Then Output shows:
--   leaderstats is not a valid member of Player "Players.YourName"

local leaderstats = player:WaitForChild("leaderstats")
local coins = leaderstats:WaitForChild("Coins") :: IntValue -- the server makes Coins an IntValue

print(`[CoinsWatcher] starting coins: {coins.Value}`)
coins.Changed:Connect(function(newValue: number)
	print(`[CoinsWatcher] coins changed to {newValue}`)
end)

This expects a server script that creates leaderstats with an IntValue named Coins, like the one in the leaderboard guide.

attempt to call a nil value

The code put brackets after something that is nil, so there was no function to run. The usual causes:

  • The function name is misspelt or its capitals differ. A module defines Format.clock and the caller writes Format.Clock(95).
  • A local function is called on a line above its definition. At that point the name does not exist yet, so move the definition up.
  • A ModuleScript never adds the function to the table it returns.

Two related messages point at the same kind of mistake. Calling a missing method on a table with a colon gives attempt to call missing method 'Kill' of table. A misspelt method on a Roblox object, such as part:Destory(), gives Destory is not a valid member of Part "Workspace.Part".

attempt to perform arithmetic (add) on ...

Luau tried to do maths (add, sub, mul, div and so on) where one side was not a number. The type names at the end of the message tell you which case you have:

  • on Instance and number: you did maths on a value object instead of its number. coins + 10 should be coins.Value + 10.
  • on nil and number: a variable or table entry was never set. Give it a starting value, or write (x or 0) where a missing value really means zero.
  • on string and number: text that is not a number, often from a TextBox. Convert it with tonumber(text) and check the result is not nil. Text that holds a number, such as "5", is converted automatically.

Comparisons fail the same way, with attempt to compare nil < number. Luau rewrites a > b as b < a, so the order in the message can be the reverse of your code.

Script in ServerScriptService named CoinDrip
local Players = game:GetService("Players")

local REWARD = 5
local INTERVAL_SECONDS = 60

local function addCoins(player: Player, amount: number)
	local leaderstats = player:FindFirstChild("leaderstats")
	local coins = leaderstats and leaderstats:FindFirstChild("Coins")
	if not (coins and coins:IsA("IntValue")) then
		-- Say why nothing happened, instead of failing silently
		warn(`[CoinDrip] {player.Name} has no leaderstats.Coins yet`)
		return
	end

	-- Broken version:
	--   coins = coins + amount
	-- coins is the IntValue object, not its number, so Output shows:
	--   attempt to perform arithmetic (add) on Instance and number
	coins.Value += amount
end

-- Every minute, give each player in the server a few coins
while true do
	task.wait(INTERVAL_SECONDS)
	for _, player in Players:GetPlayers() do
		addCoins(player, REWARD)
	end
end

attempt to concatenate string with nil

The .. operator only joins strings and numbers. Joining nil fails, and so does joining an object: "Hi " .. player gives attempt to concatenate string with Instance. Use player.Name, wrap the value in tostring(), or use string interpolation with backticks, print(`Coins: {coins}`), which prints nil instead of stopping the script.

Expected 'end' and other syntax errors

Messages that start with Expected are syntax errors: the code is not valid Luau, so Roblox cannot compile it and no line of that script runs, not even the lines above the mistake. The Script Editor underlines them in red as you type, so you can fix them before you press Play.

  • Expected 'end' (to close 'function' at line 3), got <eof>: a function, if, for or while that starts on line 3 has no matching end. The line number before the message is often the end of the file, so look at the line named in the brackets.
  • Expected 'then' when parsing if statement, got '=': a single = in a condition. Comparisons use ==.
  • Expected ')' (to close '(' at line 1), got 'local': a bracket was never closed.
  • Expected <eof>, got 'end': one end too many.

Consistent indenting makes a missing or extra end easy to spot, because every block lines up with the end that closes it.

Requested module experienced an error while loading

The first require() of a ModuleScript runs its code. If the module errors while it runs, the script that required it gets this message. The real problem is the module's own error, a line or two above in Output: fix that one. Two related messages:

  • Module code did not return exactly one value: the module does not end with a return. A ModuleScript must return one value that is not nil, usually its table.
  • Requested module was required recursively: two modules require each other. Move the part they share into a third module that both require.
ModuleScript in ReplicatedStorage named Format
local Format = {}

-- Turns a number of seconds into minutes and seconds: 95 becomes "1:35"
function Format.clock(seconds: number): string
	local whole = math.max(0, math.floor(seconds))
	return string.format("%d:%02d", whole // 60, whole % 60)
end

-- The module must end by returning its table. Without this line, every
-- require of it fails with: Module code did not return exactly one value
return Format

A script uses it with local Format = require(ReplicatedStorage:WaitForChild("Format")) and then Format.clock(95). Writing Format.Clock(95) instead is the attempt to call a nil value error.

Argument 1 missing or nil

A Roblox function received nil for an argument it needs, and the number says which argument. workspace:FindFirstChild(itemName) fails this way when itemName is nil. The bug is rarely on this line. It is wherever the value should have come from: a misspelt variable, an attribute that was never set, or a RemoteEvent argument the client did not send. RemoteEvent arguments come from the player's device, so the server should check them before using them. The RemoteEvents guide shows how.

FireServer can only be called from the client

RemoteEvents have a direction. A LocalScript calls FireServer and a server Script listens with OnServerEvent. A server Script calls FireClient or FireAllClients and LocalScripts listen with OnClientEvent. Calling a method from the wrong side gives this error, or its mirror, FireClient can only be called from the server. The fix is usually to move the code into the right kind of script: see Script vs LocalScript vs ModuleScript.

Data store error 403 in Studio

Data store calls fail in Studio with error code 403 (StudioAccessToApisNotAllowed) until you allow them. Publish the place, open File, Experience Settings, Security, turn on Enable Studio Access to API Services and save. Studio then uses the same data stores as the live game, so Roblox advises doing this in a separate test copy, not in a game people already play. Wrap every data store call in pcall, because these calls can fail for reasons outside your code. The DataStore tutorial covers saving safely.

When a script runs with no errors but does nothing

A silent script usually never ran, is still waiting for something, or ran on the wrong side. Before changing any code, make its first line print(script:GetFullName(), "started") and play. If that line never shows in Output, the problem is where the script is or how it is set up, not what it says.

Why a script can do nothing without an error
CheckWhat goes wrongFix
Script type and placeA LocalScript only runs if you put it in StarterPlayerScripts, StarterCharacterScripts, StarterGui, StarterPack or ReplicatedFirst (from the Starter containers, it runs as the player's copy). One placed in Workspace, ServerScriptService or ReplicatedStorage never runs. A Script with the default RunContext only runs in Workspace or ServerScriptService.Move it, or use the other script type. Interface and input code goes in a LocalScript; game rules go in a Script in ServerScriptService.
EnabledA script whose Enabled property is unticked does not run.Select it and tick Enabled in the Properties window.
RunContextA Script's RunContext decides where it runs. Legacy, the default, runs on the server from Workspace or ServerScriptService. Server runs on the server; Client runs on each player's device, for example from ReplicatedStorage.Set it on purpose. Roblox's docs suggest Client Scripts in ReplicatedStorage or ReplicatedFirst, and LocalScripts in the Starter containers.
The event never firesTouched only fires from physical movement, needs at least one of the two parts to be unanchored, and needs CanTouch on both. PlayerAdded does not fire for players already in the server when you connected. A RemoteEvent handler never runs if the other side never fires the event.Put a print inside the handler to prove whether it runs, then fix whatever should trigger it.
Code after a loop or a waitLines below a while true do loop never run, because the loop never ends. Lines below a WaitForChild that never finds its child never run either.Connect events before the loop, or start the loop with task.spawn. Give waits a time-out.
A check that returns quietlyA guard such as if not coins then return end stops the function without a word when the object is missing.Add a warn with the details before the return.
Output filtersOutput can be filtered by message type, by Client or Server, and by text. A leftover filter can hide the error you are looking for.Reset the filters and clear the search box.
Only you see itA LocalScript made the change, so only that player sees it. The server and other players do not.Make the change on the server. See where scripts run.

The PlayerAdded case catches many people, because in a Studio test you join almost as soon as the server starts. If anything above the Connect line yields, such as a WaitForChild, a task.wait or a require of a module that waits, you can join before the connection exists. Handle both new and existing players:

Script in ServerScriptService named Welcome
local Players = game:GetService("Players")

local function onPlayerAdded(player: Player)
	print(`[Welcome] {player.Name} joined`)
end

Players.PlayerAdded:Connect(onPlayerAdded)

-- Players who were already here when the line above ran never fire
-- PlayerAdded for this script, so handle them now
for _, player in Players:GetPlayers() do
	task.spawn(onPlayerAdded, player)
end

Debugging tools in Roblox Studio

print writes a line to Output and warn writes a warning. They are the quickest way to answer "did this run?" and "what is this value?". A bare print(x) is hard to find later, so add a label and the details that matter. typeof(x) shows whether a value is a number, a string, an Instance or nil, and GetFullName() shows exactly which object you have.

Script in ServerScriptService named ShopDebug
local Players = game:GetService("Players")

local DEBUG = true -- set to false when you are done
local TAG = "[ShopServer]"

local function debugPrint(...: any)
	if DEBUG then
		print(TAG, ...)
	end
end

Players.PlayerAdded:Connect(function(player: Player)
	debugPrint("joined:", player.Name, player.UserId)

	local leaderstats = player:WaitForChild("leaderstats", 10)
	if not leaderstats then
		warn(TAG, "no leaderstats for", player.Name, "after 10 seconds")
		return
	end

	local coins = leaderstats:FindFirstChild("Coins")
	-- typeof and ClassName show what a value really is
	debugPrint("Coins is", typeof(coins), if coins then coins.ClassName else "missing")
end)

Output's search box filters by text, so a consistent tag such as [ShopServer] lets you show only that system's lines. If you need to know how the code reached a line, debug.traceback() returns the current call stack as a string.

Breakpoints and stepping

A breakpoint pauses the game on a line so you can inspect everything at that moment. In the Script Editor, click in the gutter just to the right of a line number (a red circle appears), then start a playtest. When that line is reached, the game pauses with a yellow arrow on the next line to run. The stepping buttons then move through the code:

  • Step Over (F10) runs the current line and stops on the next one.
  • Step Into (F11) goes inside the function called on this line.
  • Step Out (Shift+F11) finishes the current function and stops back in its caller.
  • Resume Scripts carries on until the next breakpoint.

Right-click the gutter for two more useful kinds. A conditional breakpoint only pauses when an expression such as amount < 0 is true. A logpoint prints a message to Output without pausing, so you can log a value without editing the script.

Watch and Call Stack

While the game is paused, hover over a variable in the Script Editor to see its value. The Watch window lists every variable in scope on its Variables tab, and on its My Watches tab you can type any expression, such as coins.Value + amount, to see its current value. The Call Stack window shows which function called which to reach the paused line. Both open from the Script tab or the Window menu.

Script Analysis and strict mode

The Script Editor underlines problems as you type, and the Script Analysis window, opened with the Analysis button on the Script tab, lists the errors and warnings across your scripts. It catches syntax errors and unknown names before you press Play. Adding --!strict as a script's first line turns on Luau's strict type checking, which also reports misspelt properties, wrong argument types and values that might be nil. It cannot see the running game, though: a part missing from Workspace only shows up in Output.

The command bar

The command bar (on the Script tab, or Ctrl+9, ⌘9 on Mac) runs Luau straight away. While the game is running it is a fast way to ask a question: print(workspace:FindFirstChild("Bridge")) tells you whether the part exists, and print(#game:GetService("Players"):GetPlayers()) counts the players. In edit mode, command bar code changes your place for real, so be careful with anything that moves or deletes objects.

Developer Console for live games

Studio's Output only covers Studio. In a live game, press F9 or type /console in the chat to open the Developer Console. Its Log shows output from your own device, and server output if you own the game or are a group member with permission to edit it.

Roblox's built-in Assistant can also help with a single message: Roblox's own guide suggests pasting an error from Output and asking Assistant to diagnose it.

Fixing errors with RoCode

RoCode is an independent AI agent for Roblox Studio: you describe the problem in a web chat, and a Studio plugin reads and edits the place you have open. For errors, it can take over the reading steps of the routine above, working from a playtest you have already run.

You type

I pressed Play and clicking the shop button does nothing. Output shows attempt to index nil with 'Coins'. Find the cause and fix it.

RoCode does
  • Reads Studio's Output with its Check Output tool, including Server and Client lines from your last playtest, even after you press Stop
  • Opens the script named in the error with Read Script and reads the code around that line, and can use Search Scripts to find where the missing value should have been created
  • Makes a targeted edit with Edit Script, an exact search and replace in that script, then runs Check Script, which compiles it inside Studio (it does not type-check, lint or run your code)
  • Sends its changes to Studio in batches, and each batch is an undo point in Studio's history

Press Play again to confirm the fix yourself: RoCode does not start playtests, and a compile check cannot tell whether the game logic is right. How RoCode connects to Studio.

Questions

Why is my Roblox script not working when there are no errors?

Usually the script never ran (wrong script type or container, or Enabled turned off), it is still waiting on something, or the event it listens for never fires. A print on its first line tells you which. See the checklist above.

What does "infinite yield possible" mean in Roblox?

WaitForChild has waited more than 5 seconds for a child that has not appeared, and it is still waiting. The name or parent is often wrong, or nothing creates the object. More on the causes.

Why is the Output window empty or not showing?

Open it from the Window menu or the Script tab. If it is open but stays empty during a test, reset its filters and search box, then check that the scripts you expect to print are running.

Is there a Roblox script error checker?

Yes, built into Studio: the Script Editor underlines problems as you type and the Script Analysis window lists them. They find syntax and type mistakes, not missing objects or wrong game logic, which only show up when the code runs.

Should I wrap my code in pcall to stop errors?

Only calls that can fail for reasons outside your code, such as data store, HTTP or MarketplaceService requests. Wrapping a nil error in pcall hides the message, and the feature still does not work.

How do I see script errors in a live game?

Press F9 in the game, or type /console in the chat, to open the Developer Console. It shows errors from your device, and from the server if you own the game or are a group member who can edit it.

Sources and further reading

How the code was checked: every script on this page passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 30 September 2026). A type check catches misspelt APIs and wrong types, not game logic, so play-test in Studio before you publish.

Try RoCode on your own place.

Free every day, no card required. Install the plugin, describe the feature, and review what lands in Studio.