FiveM json.decode error: nil, empty string and invalid JSON fixes

json.decode fails or returns nil in your FiveM script? Why decoding nil, empty or hand-edited JSON breaks, what json.encode does to mixed keys, and how to use pcall safely.

The error appears one line after the decode:

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 gave you nothing usable, so the next line indexed nil. The decode is rarely the real fault. The input was nil, empty or broken, or the table you saved earlier was not what you thought. Here is how to find which, and how to decode safely.

1. The string is nil or empty

The most common case. The value you decode does not exist yet:

  • A database column that is NULL. A row with no saved data returns nil for that column.
  • A query with no result. MySQL.scalar.await(...) and MySQL.single.await(...) return nil when no row matches. See the oxmysql queries guide for what each function returns.
  • A KVP key that was never set. GetResourceKvpString('my_key') returns nil when the key does not exist, see FiveM resource KVP.
  • A file that is missing. LoadResourceFile(GetCurrentResourceName(), 'data.json') returns nil when the file is not there.
  • An empty string. A column saved as '' is not valid JSON either.

Decoding nil or '' is a bug in the caller, so check the string first:

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

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

2. The JSON is invalid

A string that is present can still be invalid. Typical causes when a file or a database value was edited by hand:

  • A trailing comma after the last entry: { "a": 1, }
  • Single quotes instead of double quotes: { 'a': 1 }
  • Comments in the file. JSON does not allow them.
  • A missing bracket or quote after copying part of a file.
  • Lua syntax pasted into a JSON file, such as a = 1 or [1] = 'x'.
  • A broken character from a text editor, such as curly quotes copied from a web page.
json
{
  "items": [
    { "name": "water", "count": 2 },
    { "name": "bread", "count": 1 }
  ]
}

Validate the file before you blame the script. VS Code underlines JSON errors as you type, and an online JSON validator shows the exact position of the fault.

3. Always decode with pcall

Depending on the input and the build, a bad string either raises a Lua error or comes back as nil. You do not need to remember which: wrap the call so both are handled.

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)

The type(result) ~= 'table' check also catches a value that is valid JSON but not an object, such as "hello" or 5.

Tip: when a decode fails and you do not know why, print the raw string next to its type: print(type(str), str). A nil or an empty line shows the cause at once.

4. json.encode and the keys that change

A decode can work and still give you the wrong data, because the table was saved in a form that does not survive the round trip. JSON has two shapes: a list [...] and an object {...} with text keys.

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

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

What this means in practice:

  • Numeric keys become text keys when the table is saved as an object. After decode, look the value up with the text key, or convert the key with tonumber.
  • Sparse arrays with holes ({ [1] = 'a', [3] = 'c' }) are not a clean list, so they can be written as an object instead of an array.
  • Mixed tables, with both list items and named keys, do not map cleanly to either shape. Keep a list as a list, and put named fields in a separate table.
  • Functions, userdata and vectors cannot be written as plain JSON data. Convert a vector to { x = v.x, y = v.y, z = v.z } before you encode it.
  • An empty table is usually written as [], so read it back as a list or an empty table, not as an object.

A safe way to store coordinates:

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

Use json.encode(value, { indent = true }) while debugging, since it is far easier to read.

5. Decode once, and check the type

Data from a database may reach your script as a string in one place and as a table in another. Some libraries and frameworks decode JSON columns for you, others return the raw text. Before you decode, check what you hold:

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

Decoding something that is already a table is a bug, and a type check costs nothing.

Checklist

Symptom Fix
attempt to index a nil value (local 'data') after decode The string was nil or invalid; check it before decoding and use pcall
Decoding a database column NULL and no-row results are nil; guard with if raw and raw ~= ''
A KVP read gives nothing The key was never set; use a default table
A hand-edited file fails Remove trailing commas, comments and single quotes; validate it in an editor
Number keys lost after decode JSON keys are text; read t['100'] or convert with tonumber
Vectors written as JSON Save x, y and z as separate fields

Quick answers

Why does json.decode return nil or throw an error?

The string is nil, empty, or not valid JSON. Depending on the case it either raises an error or returns nothing, so wrap the call in pcall and check the result.

Why are my numeric keys strings after json.decode?

JSON object keys are always text. A table like { [100] = true } is saved as an object with the text key 100 and comes back with the key '100', so data[100] is nil.

How do I check that a JSON file is valid?

Open it in VS Code, which marks JSON errors in the editor, or paste it into an online JSON validator. The usual faults are a trailing comma, single quotes or a comment.

Scripts that skip this problem

Item Creator V2Create usable items with animations, props, effects and more β€” without writing code.View script β†’Shop CreatorBuild a shop in under a minute β€” owners, employees, vaults and robberies included.View script β†’Quest CreatorA visual editor for quests and NPC dialogues, built node by node in game.View script β†’

Keep reading