ESX is nil: fixing esx:getSharedObject in ESX Legacy
attempt to index a nil value (global 'ESX')? The old esx:getSharedObject event is gone from ESX Legacy. Here is the one-line fix, and what to do with scripts you cannot edit.
You start your server, open the F8 console and there it is:
SCRIPT ERROR: @my_script/client/main.lua:12: attempt to index a nil value (global 'ESX')Or the server-side twin, attempt to index a nil value (upvalue 'ESX'). The script is fine, ESX is running, and still ESX is nil. This is the most common ESX error there is, and it almost always has the same cause.
Why ESX is nil
Older ESX scripts get the ESX object like this:
ESX = nil
TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end)That event was how ESX 1.1 and 1.2 handed out its shared object. ESX Legacy deprecated it, and current releases no longer answer it. The script triggers the event, nobody replies, and ESX stays nil until the first line that uses it crashes.
You will see this mostly with scripts written years ago, or copied from old tutorials.
The fix: ask es_extended directly
Replace those lines with the export ESX Legacy provides:
ESX = exports['es_extended']:getSharedObject()It works the same on the client and on the server. Do it in every file that uses ESX. If the script also had a waiting loop like this one, delete it; the export answers straight away:
-- old code: remove it
Citizen.CreateThread(function()
while ESX == nil do
TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end)
Citizen.Wait(0)
end
end)Even simpler: imports.lua
es_extended ships a small file that sets ESX up for you. Add it to the script's fxmanifest.lua:
fx_version 'cerulean'
game 'gta5'
lua54 'yes'
shared_script '@es_extended/imports.lua'
client_scripts { 'client/*.lua' }
server_scripts { 'server/*.lua' }Now every client and server file has a global ESX, and on the client ESX.PlayerData stays up to date as the player's job and money change. You can delete the getSharedObject lines entirely.
Tip: use one approach per script. Either the export at the top of each file, or
imports.luain the manifest; mixing both works, but it is noise.
Still nil? Check the start order
If you already use the export and ESX is still nil, or you get No such export getSharedObject in resource es_extended, the script is starting before es_extended. Your server.cfg decides the order:
ensure oxmysql
ensure ox_lib
ensure es_extended
ensure [esx]
ensure my_scriptTwo more things to check in the server console, from the very top:
- es_extended started without errors. If it fails (a bad
mysql_connection_string, a missing oxmysql), every script that depends on it fails too. - The folder is really called
es_extended. A renamed or duplicated copy breaks the export name, and on Linux the name is case-sensitive.
We go through that error in detail in No such export getSharedObject in resource es_extended.
Player data on the client
Old scripts also read the player like this:
-- old
ESX.GetPlayerData()
RegisterNetEvent('esx:playerLoaded')
AddEventHandler('esx:playerLoaded', function(xPlayer) PlayerData = xPlayer end)That still works in ESX Legacy, with one detail: wait until the player is loaded before you read job or money at startup.
CreateThread(function()
while not ESX.IsPlayerLoaded() do Wait(250) end
local job = ESX.GetPlayerData().job
print(('job: %s (%s)'):format(job.name, job.grade))
end)
RegisterNetEvent('esx:setJob', function(job)
-- the job changed: refresh whatever shows it
end)With imports.lua, ESX.PlayerData.job is kept current for you.
Scripts you cannot edit
If the script is escrowed (encrypted) and still uses the old event, you cannot change its code. In order of preference:
- Ask its author for an update. Any script still sold for ESX should use the export.
- Answer the old event from ESX yourself. In es_extended, add this to a server file and to a client file:
AddEventHandler('esx:getSharedObject', function(cb)
cb(ESX)
end)Warning: this is a patch to es_extended itself. Updating ESX overwrites it, so you have to add it again after every update. Keep it as a stopgap, not as the fix.
Quick checklist
| Symptom | Fix |
|---|---|
attempt to index a nil value (global 'ESX') |
Replace TriggerEvent('esx:getSharedObject'…) with exports['es_extended']:getSharedObject() |
| Still nil after the change | Start es_extended before the script in server.cfg |
No such export getSharedObject |
es_extended is not started, failed, or is named differently |
ESX.PlayerData.job is nil at startup |
Wait for ESX.IsPlayerLoaded() first |
| Escrowed script with the old event | Ask the author; meanwhile answer the event from es_extended |
Quick answers
Why is ESX nil in my script?
The script asks for ESX with the old TriggerEvent('esx:getSharedObject') call, which current ESX Legacy no longer answers. Replace it with ESX = exports['es_extended']:getSharedObject(), or add shared_script '@es_extended/imports.lua' to the fxmanifest.
Do I need to change both client and server files?
Yes. Every file that uses ESX needs the object, on the client and on the server. The imports.lua line in the fxmanifest covers all of them at once.
What if the script is escrowed and I cannot edit it?
Ask its author for an update first. As a last resort you can answer the old event from es_extended, but you have to add that back every time you update ESX.
Scripts that skip this problem
Advanced BoostingTablet-driven vehicle boosting: contracts from class D to S+, crews and a live queue.View script →
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 →