In short
for key, value in something do needs a table, such as the list GetChildren() returns. This error says something was nil. Find out why that table is missing: data that has not loaded, a function that did not return a table, or an attribute, which can never hold one. Then check it before the loop, or loop over an empty table instead.
What the message means
ServerScriptService.Inventory:21: attempt to iterate over a nil value
Other forms of the same message:
attempt to iterate over a number valueattempt to iterate over a string value
Luau lets you loop over a table directly (for k, v in inventory do). When the value after in is not a table, it has nothing to walk through and stops. With pairs(x) or ipairs(x) the same mistake gives invalid argument #1 to 'pairs' (table expected, got nil) instead.
The type in the message is what Luau found: nil most of the time, number or string when you looped over a single value by mistake.
Causes and fixes
Three things cause this error in practice. Each one below has the smallest code that triggers it, then the fix.
Player data has not loaded yet
If a table only exists after a data store load, code that runs earlier (a shop opening, a RemoteEvent) loops over nil.
local inventories = {}
local function listItems(userId)
-- before the load sets inventories[userId]: attempt to iterate over a nil value
for itemName, count in inventories[userId] do
print(itemName, count)
end
end
listItems(12345)
Check the table first and return early, or fall back to an empty table with or {} when "nothing yet" is a fine answer.
local inventories: { [number]: { [string]: number } } = {}
local function listItems(userId: number)
local inventory = inventories[userId]
if not inventory then
warn(`[Inventory] not loaded for {userId}`)
return
end
for itemName, count in inventory do
print(itemName, count)
end
end
listItems(12345)
A function did not return the table
A helper that builds a list but forgets return gives back nil. So does one that returns early on some path.
local function getSpawns()
local spawns = {}
for _, child in workspace:GetChildren() do
if child:IsA("SpawnLocation") then
table.insert(spawns, child)
end
end
-- forgot: return spawns
end
for _, spawnPart in getSpawns() do
print(spawnPart.Name)
end
Return the table on every path. A return type in the function signature makes the Script Editor warn you when one path returns nothing.
local function getSpawns(): { SpawnLocation }
local spawns: { SpawnLocation } = {}
for _, child in workspace:GetChildren() do
if child:IsA("SpawnLocation") then
table.insert(spawns, child)
end
end
return spawns
end
for _, spawnPart in getSpawns() do
print(spawnPart.Name)
end
An attribute was looped over
Attributes hold single values (text, numbers, booleans and a few Roblox types), never tables. GetAttribute returns nil when the attribute is not set, and a list saved as text is a string, so looping over it fails with nil or string in the message.
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
-- not set yet: attempt to iterate over a nil value
for _, badge in player:GetAttribute("Badges") do
print(badge)
end
end)
Keep lists in a table on the server (or in a folder of values). If you must store a list in an attribute, save it as JSON text and decode it, checking the type at each step.
local HttpService = game:GetService("HttpService")
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player: Player)
local raw = player:GetAttribute("Badges")
if typeof(raw) ~= "string" then
return -- no badges saved yet
end
local ok, list = pcall(HttpService.JSONDecode, HttpService, raw)
if ok and typeof(list) == "table" then
for _, badge in list do
print(badge)
end
end
end)
Fix it in your place
Paste the error, RoCode fixes it in your place
Copy the line from Output and paste it here. RoCode opens the script it names in the place you have open in Studio, works out which of the causes above it is, and edits the code there instead of handing you a snippet to paste.
RoCode is an AI agent for Roblox Studio: it reads the scripts in the place you have open, finds the line and edits it there, and every change is an undo point. It runs on Windows or macOS with a desktop browser. Free to start, with free credits every day.
How to find the line that causes it
- Open Output (Window menu, or the Script tab), clear it with Ctrl+K (⌘K on Mac), and press Play.
- Find the first red line. It starts with the script's place in the Explorer and the line number, such as
ServerScriptService.Shop:12:. - Click the red line. Studio opens that script with the cursor on the failing line.
- Read the lines between
Stack BeginandStack Endunder it: they list the calls that led there, newest first, so you can see which function called the broken one. - Still not sure which value is wrong? Add
print(typeof(x), x)just above the line, or set a breakpoint on it and read the values in the Watch window.
Questions
Do I need pairs or ipairs in Roblox?
No. Luau can loop over a table directly: for key, value in t do. ipairs is still useful when you want to stop at the first gap in a list.
What is the difference from table expected, got nil?
Same mistake, different wording. Looping over nil directly says attempt to iterate over a nil value; passing nil to pairs or ipairs says invalid argument #1 to 'pairs' (table expected, got nil).
How do I loop over the parts in a folder?
Use folder:GetChildren() for the direct children, or folder:GetDescendants() for everything inside, including parts inside models.
Can RoCode fix it?
Yes. Paste the Output line into RoCode. It finds why the table is missing in the script in your open place and adds the check, the return or the GetChildren call.
Sources and further reading
How the code was checked: every block marked Fixed passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 23 October 2026), and every block marked Breaks compiles and fails only when it runs. The message wording was checked by running the broken code with the Luau command line tool and against the message strings in Roblox Studio itself on 23 October 2026. A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.