In short
<, >, <= and >= need two numbers (or two strings). The message names the two types it got. nil < number means one side was missing: usually a value that was never set, a .Value you forgot, or text that needs tonumber. Note that Luau writes every comparison as < or <=, so coins >= price with a nil coins shows up as number <= nil.
What the message means
ServerScriptService.Shop:18: attempt to compare nil < number
Other forms of the same message:
attempt to compare number < nilattempt to compare string < numberattempt to compare nil <= numberattempt to compare Instance < number
Luau turns a > b into b < a before it runs it. That is why the message can list the sides in the opposite order from your code: read it as "one side was nil, the other a number", then check both.
== and ~= never cause this error. They work on any two values and simply give false or true.
Causes and fixes
Three things cause this error in practice. Each one below has the smallest code that triggers it, then the fix.
- One side was never set (nil)
- Text was compared with a number
- A value object was compared instead of its number
One side was never set (nil)
A value from a table, an attribute or a function that returned nothing is nil. A shop check like if coins >= price then fails for a player whose data has not loaded, or for an item with no price.
local prices = { Sword = 100 }
local function canBuy(coins, item)
-- "Bow" has no price: attempt to compare nil <= number
return coins >= prices[item]
end
print(canBuy(150, "Bow"))
Check that both values exist before comparing, and decide what a missing one means.
local prices: { [string]: number } = { Sword = 100 }
local function canBuy(coins: number, item: string): boolean
local price = prices[item]
if price == nil then
warn(`[Shop] no price for {item}`)
return false
end
return coins >= price
end
print(canBuy(150, "Bow"))
Text was compared with a number
A TextBox, an attribute or a string from a data store holds text. Unlike maths, comparisons never convert text, so "50" < 100 fails with string < number even though the text looks like a number.
local box = script.Parent
box.FocusLost:Connect(function()
-- attempt to compare string < number
if box.Text < 100 then
print("small bet")
end
end)
Convert with tonumber, and handle the case where it is not a number.
local box = script.Parent :: TextBox
box.FocusLost:Connect(function()
local bet = tonumber(box.Text)
if bet and bet < 100 then
print("small bet")
end
end)
A value object was compared instead of its number
leaderstats.Coins is an IntValue, not a number. Coins >= 50 compares an object with a number and fails with Instance in the message.
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
local coins = player:WaitForChild("leaderstats"):WaitForChild("Coins")
-- attempt to compare number <= Instance
if coins >= 50 then
print("rich")
end
end)
Compare .Value.
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player: Player)
local leaderstats = player:WaitForChild("leaderstats", 10)
local coins = leaderstats and leaderstats:WaitForChild("Coins", 10)
if coins and coins:IsA("IntValue") and coins.Value >= 50 then
print("rich")
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
Why does the message show the sides in the wrong order?
Luau runs every greater-than check as a less-than check with the two sides swapped, so a >= b runs as b <= a. The message describes what actually ran, so the nil can appear on the other side from your code.
Why does "50" + 1 work but "50" < 100 fail?
Luau converts number-like text in maths, but never in comparisons. Use tonumber before comparing.
Can I compare two strings?
Yes, "apple" < "banana" compares them alphabetically. The error only comes when the two sides are different types, or a type that cannot be ordered, such as nil, a boolean or a table.
Can RoCode fix it?
Yes. Paste the Output line into RoCode. It finds which side of the comparison is wrong in the script in your open place and adds the missing check or conversion.
Sources and further reading
- Roblox Creator Docs: operators. relational operators
- Roblox Creator Docs: tonumber
- Luau: syntax
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, including the swapped order for > and >=, 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.