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

Corregir 'attempt to compare number with nil' y 'attempt to perform arithmetic on a nil value' en FiveM: datos de jugador sin cargar, valores de config faltantes, tonumber y valores seguros predeterminados con `or`.

Salida de consola:

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')

Una línea del script usa un valor que aún no existe en una comparación o un cálculo. Aquí está lo que generalmente deja el valor vacío, y cómo hacer el script seguro.

Qué significa el error

Lua te da nil para cualquier variable o entrada de tabla que no tenga valor. Comparar (<, >, <=) o calcular (+, -, *, /) con nil detiene el script con uno de estos mensajes:

  • attempt to compare number with nil (o attempt to compare nil with number): un lado de la comparación es nil.
  • attempt to perform arithmetic on a nil value: un cálculo usa una variable nil. Cuando Lua conoce el nombre, lo agrega, como (field 'money') o (local 'price').

El número de línea en el mensaje es la línea con la comparación o la suma. El nil es uno de los valores en esa línea. Imprímelos para ver cuál es:

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

1. Los datos del jugador aún no se cargan

La causa más común: el script lee el jugador antes de que el framework haya terminado de cargarlo, por ejemplo en las primeras líneas de un archivo, o en un hilo que comienza a la vez.

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

Espera el evento cargado del framework, y lee los datos desde allí:

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)

En el servidor, un objeto de jugador también puede ser nil cuando la id es incorrecta o el jugador ha salido. Verifica antes de usarlo:

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

En QBox, el equivalente es exports.qbx_core:GetPlayer(source), con la misma verificación nil.

Consejo: si el script se reinicia mientras estés en el servidor, playerLoaded ya se ha disparado y no se disparará nuevamente. Lee los datos una vez cuando el recurso comienza también.

2. Falta un valor de config

Agregaste una línea a config.lua que el script no tiene, o el script lee una clave que tu config anterior no contiene. Después de una actualización, un config.lua antiguo es una causa muy común.

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

Compara tu config con la que se envía con la nueva versión, y agrega las claves faltantes. En tu propio código, da a cada configuración un valor predeterminado:

lua
local price = Config.Price or 100

3. El valor es una cadena, no un número

Los valores de una base de datos, un cuadro de entrada en un menú o un mensaje JSON a menudo llegan como texto. Comparar una cadena con un número da un error diferente (attempt to compare number with string), pero una conversión que falla te da nil, que trae este error nuevamente. tonumber convierte, y retorna nil cuando no puede:

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

4. Usa or para un valor predeterminado

or retorna el lado derecho cuando el lado izquierdo es nil (o false). Es la forma más corta de hacer un cálculo seguro:

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

Úsalo donde un valor faltante realmente puede significar cero, como un bonificador opcional. Donde un valor faltante es un error, por ejemplo un precio que debe existir, detente con un mensaje claro en lugar de ocultarlo:

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. Un resultado de base de datos o devolución de llamada está vacío

Una consulta que no encuentra ninguna fila da nil o una tabla vacía. Leer una columna de ella falla:

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

Verifica la fila primero:

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

Lo mismo aplica para las devoluciones de llamada del servidor: la respuesta puede ser nil si la devolución de llamada no fue encontrada o no retornó nada. Si tu script espera en una devolución de llamada, maneja ese caso. Relacionado: attempt to call a nil value es el error hermano cuando una función, no un valor, falta.

6. Un error tipográfico en un nombre de campo

xPlayer.money y xPlayer.Money son diferentes. Al igual que price y Price. El mensaje de error a menudo nombra el campo: verifica su ortografía contra la documentación del framework o contra la tabla que imprimes con print(json.encode(data)).

Lista de verificación

Síntoma Solución
Error justo después de unirse Espera el evento del jugador cargado antes de leer datos del jugador
xPlayer o Player es nil en el servidor Verifica if not xPlayer then return end y el id de source
Error después de actualizar un script Agrega las nuevas claves del config.lua enviado al tuyo
El valor viene de un menú o JSON Convierte con tonumber(value) y verifica que no sea nil
Un valor puede estar faltando a propósito Usa value or 0 como valor predeterminado
La consulta de base de datos retorna nada Verifica que la fila no sea nil antes de leer sus columnas
El campo es nil aunque debería existir Verifica la ortografía y las mayúsculas

Respuestas rápidas

¿Qué significa 'attempt to compare number with nil'?

Una línea compara un número con algo que no tiene valor, como price > 100 cuando price es nil. Encuentra qué variable es nil y por qué nunca se estableció.

¿Cómo doy a una variable un valor predeterminado en Lua?

Usa or: local count = tonumber(value) or 0. Si el valor es nil o no es un número, count se convierte en 0.

¿Por qué solo ocurre justo después de que me uno al servidor?

El script lee los datos del jugador antes de que el framework los haya cargado. Espera el evento jugador cargado, o verifica que el objeto jugador no sea nil antes de leerlo.

Scripts que evitan este problema

Shop CreatorCrea una tienda en menos de un minuto — dueños, empleados, caja fuerte y atracos.Ver script →Pawn Shop AppUn mercado de empeños entre jugadores dentro de lb-phone.Ver script →Quest CreatorUn editor visual de misiones y diálogos con NPC, nodo a nodo dentro del juego.Ver script →

Sigue leyendo