Error en FiveM json.decode: soluciones para nil, cadena vacía y JSON inválido

¿json.decode falla o devuelve nil en tu script de FiveM? Por qué decodificar nil, JSON vacío o editado a mano se rompe, qué hace json.encode con claves mixtas, y cómo usar pcall de forma segura.

El error aparece una línea después de la decodificación:

text
SCRIPT ERROR: @my_script/server/main.lua:31: attempt to index a nil value (local 'data')
lua
local data = json.decode(row.metadata)
print(data.level)   -- data is nil

json.decode no te dio nada utilizable, así que la siguiente línea indexó nil. La decodificación raramente es la culpa real. La entrada era nil, estaba vacía o rota, o la tabla que guardaste antes no era lo que pensabas. Aquí está cómo encontrar cuál, y cómo decodificar de forma segura.

1. La cadena es nil o está vacía

El caso más común. El valor que decodificas no existe aún:

  • Una columna de base de datos que es NULL. Una fila sin datos guardados devuelve nil para esa columna.
  • Una consulta sin resultado. MySQL.scalar.await(...) y MySQL.single.await(...) devuelven nil cuando no coincide ninguna fila. Ver la guía de consultas oxmysql para lo que cada función devuelve.
  • Una clave KVP que nunca se estableció. GetResourceKvpString('my_key') devuelve nil cuando la clave no existe, ver KVP de recurso FiveM.
  • Un archivo que está faltando. LoadResourceFile(GetCurrentResourceName(), 'data.json') devuelve nil cuando el archivo no está ahí.
  • Una cadena vacía. Una columna guardada como '' tampoco es JSON válido.

Decodificar nil o '' es un bug en la persona que llama, así que comprueba la cadena primero:

lua
local raw = GetResourceKvpString('my_key')
local data = {}

if raw and raw ~= '' then
    data = json.decode(raw) or {}
end

2. El JSON es inválido

Una cadena que está presente aún puede ser inválida. Las causas típicas cuando un archivo o valor de base de datos se editó a mano:

  • Una coma al final después de la última entrada: { "a": 1, }
  • Comillas simples en lugar de comillas dobles: { 'a': 1 }
  • Comentarios en el archivo. JSON no los permite.
  • Una llave o comilla faltante después de copiar parte de un archivo.
  • Sintaxis Lua pegada en un archivo JSON, como a = 1 o [1] = 'x'.
  • Un carácter roto de un editor de texto, como comillas rizadas copiadas de una página web.
json
{
  "items": [
    { "name": "water", "count": 2 },
    { "name": "bread", "count": 1 }
  ]
}

Valida el archivo antes de culpar al script. VS Code subraya errores JSON mientras escribes, y un validador JSON en línea muestra la posición exacta del error.

3. Siempre decodifica con pcall

Dependiendo de la entrada y la compilación, una cadena mala ya sea lanza un error Lua o vuelve como nil. No necesitas recordar cuál: envuelve la llamada así ambos se manejan.

lua
local function safeDecode(str)
    if type(str) ~= 'string' or str == '' then
        return nil
    end

    local ok, result = pcall(json.decode, str)
    if not ok or type(result) ~= 'table' then
        return nil
    end

    return result
end

local data = safeDecode(row.metadata) or {}
print(data.level)

La comprobación type(result) ~= 'table' también atrapa un valor que es JSON válido pero no un objeto, como "hello" o 5.

Consejo: cuando una decodificación falla y no sabes por qué, imprime la cadena sin procesar junto a su tipo: print(type(str), str). Un nil o una línea vacía muestra la causa de una vez.

4. json.encode y las claves que cambian

Una decodificación puede funcionar y aún darte datos incorrectos, porque la tabla se guardó de una forma que no sobrevive el viaje de ida y vuelta. JSON tiene dos formas: una lista [...] y un objeto {...} con claves de texto.

lua
local saved = json.encode({ [100] = 'a', [250] = 'b' })
local loaded = json.decode(saved)

print(loaded[100])     -- nil
print(loaded['100'])   -- 'a'

Lo que esto significa en la práctica:

  • Las claves numéricas se convierten en claves de texto cuando la tabla se guarda como un objeto. Después de decodificar, busca el valor con la clave de texto, o convierte la clave con tonumber.
  • Arrays dispersos con huecos ({ [1] = 'a', [3] = 'c' }) no son una lista limpia, así que pueden escribirse como un objeto en lugar de un array.
  • Tablas mixtas, con elementos de lista y claves nombradas, no se asignan limpiamente a ninguna forma. Mantén una lista como una lista, y pon campos nombrados en una tabla separada.
  • Funciones, userdata y vectores no pueden escribirse como datos JSON simples. Convierte un vector a { x = v.x, y = v.y, z = v.z } antes de codificarlo.
  • Una tabla vacía generalmente se escribe como [], así que léela de vuelta como una lista o una tabla vacía, no como un objeto.

Una forma segura de guardar coordenadas:

lua
-- save
local payload = json.encode({ x = coords.x, y = coords.y, z = coords.z })

-- load
local pos = safeDecode(payload)
if pos then
    SetEntityCoords(ped, pos.x, pos.y, pos.z, false, false, false, false)
end

Usa json.encode(value, { indent = true }) mientras debuggeas, ya que es mucho más fácil de leer.

5. Decodifica una vez, y comprueba el tipo

Los datos de una base de datos pueden llegar a tu script como una cadena en un lugar y como una tabla en otro. Algunas bibliotecas y frameworks decodifican columnas JSON por ti, otras devuelven el texto sin procesar. Antes de decodificar, comprueba lo que tienes:

lua
local meta = row.metadata
if type(meta) == 'string' then
    meta = safeDecode(meta)
end
meta = meta or {}

Decodificar algo que ya es una tabla es un bug, y una comprobación de tipo no cuesta nada.

Checklist

Síntoma Solución
attempt to index a nil value (local 'data') después de decodificar La cadena era nil o inválida; compruébala antes de decodificar y usa pcall
Decodificando una columna de base de datos NULL y resultados sin filas son nil; protege con if raw and raw ~= ''
Una lectura KVP no da nada La clave nunca se estableció; usa una tabla por defecto
Un archivo editado a mano falla Elimina comas finales, comentarios y comillas simples; valídalo en un editor
Claves de número perdidas después de decodificar Las claves JSON son texto; lee t['100'] o convierte con tonumber
Vectores escritos como JSON Guarda x, y y z como campos separados

Respuestas rápidas

¿Por qué json.decode devuelve nil o lanza un error?

La cadena es nil, está vacía, o no es JSON válido. Dependiendo del caso, ya sea lanza un error o no devuelve nada, así que envuelve la llamada en pcall y comprueba el resultado.

¿Por qué mis claves numéricas son cadenas después de json.decode?

Las claves de objetos JSON siempre son texto. Una tabla como { [100] = true } se guarda como un objeto con la clave de texto 100 y vuelve con la clave '100', así que data[100] es nil.

¿Cómo compruebo que un archivo JSON es válido?

Abrelo en VS Code, que marca errores JSON en el editor, o pégalo en un validador JSON en línea. Los errores usuales son una coma al final, comillas simples o un comentario.

Scripts que evitan este problema

Item Creator V2Crea items usables con animaciones, props, efectos y más — sin escribir código.Ver script →Shop CreatorCrea una tienda en menos de un minuto — dueños, empleados, caja fuerte y atracos.Ver script →Quest CreatorUn editor visual de misiones y diálogos con NPC, nodo a nodo dentro del juego.Ver script →

Sigue leyendo