tentar comparar número com nil / aritmética em um valor nil: corrigir

Corrija 'attempt to compare number with nil' e 'arithmetic on a nil value' no FiveM: dados do jogador não carregados, valores de config faltando, tonumber e padrões seguros com `or`.

Saída do console:

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

Uma linha do script usa um valor que ainda não existe em uma comparação ou um cálculo. Aqui está o que geralmente deixa o valor vazio, e como tornar o script seguro.

O que o erro significa

Lua lhe dá nil para qualquer variável ou entrada de tabela que não tem valor. Comparar (<, >, <=) ou calcular (+, -, *, /) com nil para o script com uma dessas mensagens:

  • attempt to compare number with nil (ou attempt to compare nil with number): um lado da comparação é nil.
  • attempt to perform arithmetic on a nil value: um cálculo usa uma variável nil. Quando Lua conhece o nome, o adiciona, como (field 'money') ou (local 'price').

O número de linha na mensagem é a linha com a comparação ou a soma. O nil é um dos valores nessa linha. Imprima-os para ver qual é:

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

1. Os dados do jogador ainda não foram carregados

A causa mais comum: o script lê o jogador antes do framework ter terminado de carregá-los, por exemplo nas primeiras linhas de um arquivo, ou em uma thread que inicia imediatamente.

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

Aguarde o evento de carregamento do framework e leia os dados de lá:

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)

No servidor, um objeto de jogador também pode ser nil quando o id está errado ou o jogador saiu. Verifique antes de usá-lo:

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

No QBox, o equivalente é exports.qbx_core:GetPlayer(source), com a mesma verificação nil.

Dica: se o script for reiniciado enquanto você estiver no servidor, playerLoaded já foi disparado e não disparará novamente. Leia os dados uma vez quando o recurso iniciar também.

2. Um valor de config está faltando

Você adicionou uma linha a config.lua que o script não tem, ou o script lê uma chave que seu config mais antigo não contém. Após uma atualização, um config.lua antigo é uma causa muito comum.

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

Compare seu config com aquele enviado com a nova versão, e adicione as chaves faltando. Em seu próprio código, dê a cada configuração um padrão:

lua
local price = Config.Price or 100

3. O valor é uma string, não um número

Valores de um banco de dados, uma caixa de entrada em um menu ou uma mensagem JSON frequentemente chegam como texto. Comparar uma string com um número dá um erro diferente (attempt to compare number with string), mas uma conversão que falha dá este erro de novo. tonumber converte, e retorna nil quando não consegue:

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

4. Use or para um padrão

or retorna o lado direito quando o lado esquerdo é nil (ou false). É a forma mais curta de tornar um cálculo seguro:

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

Use-o onde um valor faltando realmente pode significar zero, como um bônus opcional. Onde um valor faltando é um bug, por exemplo um preço que deve existir, pare com uma mensagem clara em vez de ocultá-lo:

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. Um resultado de banco de dados ou callback está vazio

Uma consulta que não encontra linha dá nil ou uma tabela vazia. Ler uma coluna dela falha:

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

Verifique a linha primeiro:

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

O mesmo vai para callbacks de servidor: a resposta pode ser nil se o callback não foi encontrado ou retornou nada. Se seu script aguarda um callback, lide com esse caso. Relacionado: tentar chamar um valor nil é o erro irmão quando uma função, não um valor, está faltando.

6. Um erro de digitação em um nome de campo

xPlayer.money e xPlayer.Money são diferentes. Assim como price e Price. A mensagem de erro frequentemente nomeia o campo: verifique sua ortografia contra a documentação do framework ou contra a tabela que você imprime com print(json.encode(data)).

Checklist

Sintoma Correção
Erro logo após entrar Aguarde o evento de jogador carregado antes de ler dados do jogador
xPlayer ou Player é nil no servidor Verifique if not xPlayer then return end e a id de fonte
Erro após atualizar um script Adicione as novas chaves do config.lua enviado ao seu
Valor vem de um menu ou JSON Converta com tonumber(value) e verifique se não é nil
Um valor pode estar faltando intencionalmente Use value or 0 como um padrão
Consulta de banco de dados retorna nada Verifique se a linha não é nil antes de ler suas colunas
Campo é nulo mesmo que deveria existir Verifique a ortografia e letras maiúsculas

Respostas rápidas

O que significa 'attempt to compare number with nil'?

Uma linha compara um número com algo que não tem valor, como price > 100 quando price é nil. Encontre qual variável é nil e por que nunca foi definida.

Como dou a uma variável um valor padrão em Lua?

Use or: local count = tonumber(value) or 0. Se o valor é nil ou não é um número, count se torna 0.

Por que só acontece logo após entrar no servidor?

O script lê dados do jogador antes do framework tê-los carregado. Aguarde o evento de jogador carregado, ou verifique se o objeto do jogador não é nil antes de lê-lo.

Scripts sem esse problema

Shop CreatorMonte uma loja em menos de um minuto — donos, funcionários, cofres e assaltos inclusos.Ver script →Pawn Shop AppUma casa de penhores entre jogadores dentro do lb-phone.Ver script →Quest CreatorUm editor visual de missões e diálogos com NPCs, montado nó por nó dentro do jogo.Ver script →

Continue lendo