attempt to compare number with nil / arithmetic on a nil value: fix

Fix 'attempt to compare number with nil' and 'arithmetic on a nil value' in FiveM: unloaded player data, missing config values, tonumber and safe defaults with `or`.

Console output:

text
SCRIPT ERROR: @my_script/server/main.lua:58: attempt to compare number with nil
SCRIPT ERROR: @my_script/client/main.lua:91: attempt to perform arithmetic on a nil value (field 'money')

A line of the script uses a value that does not exist yet in a comparison or a calculation. Here is what usually leaves the value empty, and how to make the script safe.

What the error means

Lua gives you nil for any variable or table entry that has no value. Comparing (<, >, <=) or calculating (+, -, *, /) with nil stops the script with one of these messages:

  • attempt to compare number with nil (or attempt to compare nil with number): one side of the comparison is nil.
  • attempt to perform arithmetic on a nil value: a calculation uses a nil variable. When Lua knows the name, it adds it, like (field 'money') or (local 'price').

The line number in the message is the line with the comparison or the sum. The nil is one of the values on that line. Print them to see which one:

lua
print('price', price, type(price))
print('count', count, type(count))
if price > count then
    -- ...
end

1. Player data is not loaded yet

The most common cause: the script reads the player before the framework has finished loading them, for example in the first lines of a file, or in a thread that starts at once.

lua
-- runs too early on the client: money is nil
local money = ESX.GetPlayerData().money
if money > 100 then
    -- ...
end

Wait for the framework's loaded event, and read the data from there:

lua
-- ESX client
local PlayerData = {}

RegisterNetEvent('esx:playerLoaded', function(xPlayer)
    PlayerData = xPlayer
end)
lua
-- QBCore client
local QBCore = exports['qb-core']:GetCoreObject()
local PlayerData = {}

RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
    PlayerData = QBCore.Functions.GetPlayerData()
end)

On the server, a player object can also be nil when the id is wrong or the player has left. Check it before you use it:

lua
local xPlayer = ESX.GetPlayerFromId(source)
if not xPlayer then return end
lua
local Player = QBCore.Functions.GetPlayer(source)
if not Player then return end

On QBox, the equivalent is exports.qbx_core:GetPlayer(source), with the same nil check.

Tip: if the script is restarted while you are on the server, playerLoaded has already fired and will not fire again. Read the data once when the resource starts as well.

2. A config value is missing

You added a line to config.lua that the script does not have, or the script reads a key that your older config does not contain. After an update, an old config.lua is a very common cause.

lua
-- config.lua of the old version has no Config.Price
if playerMoney >= Config.Price then
    -- ...
end

Compare your config with the one shipped with the new version, and add the missing keys. In your own code, give every setting a default:

lua
local price = Config.Price or 100

3. The value is a string, not a number

Values from a database, an input box in a menu or a JSON message often arrive as text. Comparing a string with a number gives a different error (attempt to compare number with string), but a conversion that fails gives you nil, which brings this error back. tonumber converts, and returns nil when it cannot:

lua
local amount = tonumber(data.amount)
if not amount then
    return -- not a number: stop here
end

4. Use or for a default

or returns the right side when the left side is nil (or false). It is the shortest way to make a calculation safe:

lua
local count = tonumber(data.count) or 0
local total = (item.price or 0) * count

Use it where a missing value really can mean zero, such as an optional bonus. Where a missing value is a bug, for example a price that must exist, stop with a clear message instead of hiding it:

lua
local price = Config.Items[name] and Config.Items[name].price
if not price then
    print(('no price set for %s'):format(name))
    return
end

5. A database or callback result is empty

A query that finds no row gives nil or an empty table. Reading a column from it fails:

lua
local row = MySQL.single.await('SELECT balance FROM bank WHERE owner = ?', { identifier })
if row.balance > 0 then   -- row is nil when the player has no row
    -- ...
end

Check the row first:

lua
local row = MySQL.single.await('SELECT balance FROM bank WHERE owner = ?', { identifier })
local balance = row and row.balance or 0

The same goes for server callbacks: the answer can be nil if the callback was not found or returned nothing. If your script waits on a callback, handle that case. Related: attempt to call a nil value is the sister error when a function, not a value, is missing.

6. A typo in a field name

xPlayer.money and xPlayer.Money are different. So are price and Price. The error message often names the field: check its spelling against the framework documentation or against the table you print with print(json.encode(data)).

Checklist

Symptom Fix
Error right after joining Wait for the player loaded event before reading player data
xPlayer or Player is nil on the server Check if not xPlayer then return end and the source id
Error after updating a script Add the new keys of the shipped config.lua to yours
Value comes from a menu or JSON Convert with tonumber(value) and check it is not nil
A value may be missing on purpose Use value or 0 as a default
Database query returns nothing Check the row is not nil before reading its columns
Field is nil even though it should exist Check the spelling and capital letters

Quick answers

What does 'attempt to compare number with nil' mean?

A line compares a number with something that has no value, like price > 100 when price is nil. Find which variable is nil and why it was never set.

How do I give a variable a default value in Lua?

Use or: local count = tonumber(value) or 0. If the value is nil or not a number, count becomes 0.

Why does it only happen right after I join the server?

The script reads player data before the framework has loaded it. Wait for the player loaded event, or check that the player object is not nil before you read from it.

Scripts that skip this problem

Shop CreatorBuild a shop in under a minute — owners, employees, vaults and robberies included.View script →Pawn Shop AppA player-to-player pawn market inside lb-phone.View script →Quest CreatorA visual editor for quests and NPC dialogues, built node by node in game.View script →

Keep reading