attempt to index a nil value (local 'Player'): QBCore and QBox fix

Player is nil in your QBCore or QBox script? Why QBCore.Functions.GetPlayer(source) returns nil, citizenid lookups for online players only, and the safe guard patterns.

In the server console:

text
SCRIPT ERROR: @my_script/server/main.lua:27: attempt to index a nil value (local 'Player')
lua
local Player = QBCore.Functions.GetPlayer(source)
Player.Functions.AddMoney('cash', 100)   -- Player is nil

GetPlayer found no loaded player for that id and returned nil, and the next line indexed it. The causes mirror the ESX case in ESX xPlayer is nil, with QBCore and QBox specifics.

1. The player is not loaded yet

A player can be connected before QBCore has loaded their character, especially while a multicharacter screen is open. Until the character is chosen, GetPlayer(source) returns nil.

Code that runs on playerJoining, or from a thread that starts immediately, hits this. Wait for the QBCore event that fires when the player is ready. On the server:

lua
AddEventHandler('QBCore:Server:PlayerLoaded', function(Player)
    print(Player.PlayerData.citizenid .. ' is loaded')
end)

On the client, the matching event is QBCore:Client:OnPlayerLoaded:

lua
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
    PlayerLoaded = true
end)

If your resource restarts while players are online, those events do not fire again for them. On start, go through the players who are already loaded:

lua
CreateThread(function()
    for _, src in pairs(QBCore.Functions.GetPlayers()) do
        local Player = QBCore.Functions.GetPlayer(src)
        if Player then
            -- set up the player
        end
    end
end)

2. source changed after a Wait

source is a global that FiveM sets for the running event. If you call Wait inside the handler, another event can run and change it.

lua
RegisterNetEvent('my_script:sell', function()
    Wait(1000)
    local Player = QBCore.Functions.GetPlayer(source)   -- may be wrong, or nil
end)

Store it first:

lua
RegisterNetEvent('my_script:sell', function()
    local src = source
    Wait(1000)

    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end
end)

3. The id is wrong

GetPlayer expects the server id of an online player, as a number.

  • A string from a command. Arguments are text, so convert them: QBCore.Functions.GetPlayer(tonumber(args[1])).
  • The wrong id from the client. On the client, PlayerId() is a local index. The server id is GetPlayerServerId(PlayerId()). Better, ignore ids sent by the client and use source, see securing server events.
  • The console. A command typed in the server console has source equal to 0, which is not a player.
  • An id that has left. The player disconnected while your code waited.
lua
QBCore.Commands.Add('givecash', 'Give cash', { { name = 'id', help = 'Player id' }, { name = 'amount', help = 'Amount' } }, true, function(source, args)
    local target = QBCore.Functions.GetPlayer(tonumber(args[1]))
    local amount = tonumber(args[2])
    if not target or not amount then return end

    target.Functions.AddMoney('cash', amount)
end, 'admin')

4. citizenid lookups only find online players

Many scripts store the citizenid instead of the source, because the source changes at every session. To get the player from a citizenid:

lua
local Player = QBCore.Functions.GetPlayerByCitizenId(citizenid)

This only works while that player is online and loaded. For a player who is offline it returns nil, which is not an error in your data. Read the database for offline players:

lua
local row = MySQL.single.await('SELECT charinfo, job FROM players WHERE citizenid = ?', { citizenid })
if row then
    local charinfo = json.decode(row.charinfo)
end

charinfo is stored as JSON text, so decode it safely, see FiveM json.decode errors. Writing to an offline player is a different task: change the database row only if the player is really offline, or the next save from the server overwrites your change.

5. On QBox

QBox's core is qbx_core. It keeps a compatibility layer, so QBCore.Functions.GetPlayer still works in scripts written for QBCore. For new code, use its exports, which return the same kind of player object or nil:

lua
local player = exports.qbx_core:GetPlayer(source)
if not player then return end

print(player.PlayerData.citizenid)

For an online player by citizenid:

lua
local player = exports.qbx_core:GetPlayerByCitizenId(citizenid)

The rules from this article apply in the same way: no player until the character is loaded, store source before a Wait, and expect nil for an offline player. If you are moving a script between the cores, see converting a QBCore script to QBox.

6. playerDropped

When a player leaves, qb-core saves and removes the player on its own, and the order of handlers for playerDropped is not something to rely on. In your own playerDropped handler, GetPlayer(source) can already be nil. Keep what you need in your own table while the player is online:

lua
local sessions = {}

AddEventHandler('QBCore:Server:PlayerLoaded', function(Player)
    sessions[Player.PlayerData.source] = Player.PlayerData.citizenid
end)

AddEventHandler('playerDropped', function()
    local citizenid = sessions[source]
    sessions[source] = nil
    if citizenid then
        print(citizenid .. ' left')
    end
end)

The guard pattern

Start every server handler that needs a player in the same way:

lua
RegisterNetEvent('my_script:action', function()
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    -- Player is safe to use here
end)

Tip: if the error is about QBCore itself being nil, the problem is the core object, not the player. See attempt to index a nil value (global 'QBCore').

Checklist

Symptom Fix
Nil right after the player joins Wait for QBCore:Server:PlayerLoaded instead of playerJoining
Nil after a Wait Store local src = source and use src
Nil from a command Convert the argument with tonumber; the console source is 0
GetPlayerByCitizenId returns nil The player is offline; query the players table
QBox server Use exports.qbx_core:GetPlayer(source) and check for nil
Nil in playerDropped Keep the data you need in your own table while the player is online
Any nil Add if not Player then return end at the top of the handler

Quick answers

Why does QBCore.Functions.GetPlayer return nil?

There is no loaded player with that id. The player may still be loading, the id may be wrong or lost after a Wait, or the player has already left.

Does GetPlayerByCitizenId work for offline players?

No. It only finds players who are online. For an offline player, read the players table in the database instead.

What is the QBox equivalent of QBCore.Functions.GetPlayer?

exports.qbx_core:GetPlayer(source). It returns the player object, or nil when there is no loaded player for that id.

Scripts that skip this problem

Advanced BoostingTablet-driven vehicle boosting: contracts from class D to S+, crews and a live queue.View script →Drug Dealer AppStreet sales as an lb-phone app: zones, NPC buyers, levels and police alerts.View script →Pawn Shop AppA player-to-player pawn market inside lb-phone.View script →

Keep reading