FixFX

Troubleshooting

Common issues and solutions for ESX framework.

This guide covers common issues encountered when working with ESX framework and their solutions.

Installation Issues

Database Connection Problems

Error: "Access denied for user"

Symptoms:

[ERROR] Access denied for user 'esx_user'@'localhost' (using password: YES)

Solutions:

  1. Check Database Credentials:
-- Verify user exists
SELECT User, Host FROM mysql.user WHERE User = 'esx_user';
 
-- If user doesn't exist, create it
CREATE USER 'esx_user'@'localhost' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON esx_server.* TO 'esx_user'@'localhost';
FLUSH PRIVILEGES;
  1. Verify Connection String:
# In server.cfg, ensure connection string is correct
set mysql_connection_string "mysql://esx_user:your_password@localhost/esx_server?charset=utf8mb4"
  1. Check MySQL Service:
# Windows
net start mysql80
 
# Linux
sudo systemctl start mysql
sudo systemctl status mysql

Error: "Database 'esx_server' doesn't exist"

Solution:

CREATE DATABASE esx_server CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Resource Loading Issues

Error: "Resource [es_extended] couldn't be started"

Symptoms:

[ERROR] Could not load resource es_extended
[ERROR] Failed to start resource es_extended

Solutions:

  1. Check Resource Path:
resources/
├── [esx]/
│   └── es_extended/    # Should be here
├── [standalone]/
└── [voice]/
  1. Verify fxmanifest.lua:
-- Check for syntax errors in fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
-- Ensure proper structure
  1. Check Dependencies:
# Ensure database connector loads before es_extended
ensure oxmysql
ensure es_extended

Error: "ESX object is nil"

Symptoms:

  • Scripts can't access ESX functions
  • Error: "attempt to index a nil value (global 'ESX')"

Solutions:

  1. Proper ESX Initialization:
-- Modern method (recommended)
ESX = exports['es_extended']:getSharedObject()
 
-- Legacy method (still works)
ESX = nil
TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end)
 
-- Using imports (ESX Legacy)
-- Add @es_extended/imports.lua to shared_scripts
  1. Wait for ESX to Load:
-- If getting nil, wait for ESX
ESX = nil
Citizen.CreateThread(function()
    while ESX == nil do
        TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end)
        Citizen.Wait(0)
    end
end)

Database Schema Issues

Error: "Table doesn't exist"

Symptoms:

  • MySQL errors about missing tables
  • Resources fail to start

Solutions:

  1. Import Missing Tables:
# Import base ESX schema
mysql -u esx_user -p esx_server < [esx]/es_extended/installation/esx_legacy.sql
 
# Import resource-specific tables
mysql -u esx_user -p esx_server < [esx]/esx_society/installation/esx_society.sql
mysql -u esx_user -p esx_server < [esx]/esx_datastore/installation/esx_datastore.sql
  1. Check Required Tables:
-- Verify essential tables exist
SHOW TABLES;
 
-- Should include:
-- users
-- jobs
-- job_grades
-- vehicles
-- owned_vehicles
-- user_accounts
-- user_inventory

Player Data Issues

Player Data Not Loading

Error: "Player data is nil"

Symptoms:

  • Player spawns but has no data
  • Commands don't work
  • Money/job shows as nil

Solutions:

  1. Check Player Loading Events:
-- Client-side
RegisterNetEvent('esx:playerLoaded', function(xPlayer)
    ESX.PlayerData = xPlayer
    print('Player data loaded:', json.encode(xPlayer))
end)
  1. Verify Database Schema:
-- Check if users table exists and has data
SELECT COUNT(*) FROM users;
DESCRIBE users;
  1. Check for Database Corruption:
-- Look for corrupted player data
SELECT identifier, name, LENGTH(accounts) as accounts_length 
FROM users 
WHERE accounts IS NULL OR accounts = '' OR JSON_VALID(accounts) = 0;

Error: "Player already exists"

Symptoms:

  • Cannot create new character
  • Gets stuck on character creation

Solutions:

  1. Check for Duplicate Identifiers:
-- Find duplicate identifiers
SELECT identifier, COUNT(*) as count 
FROM users 
GROUP BY identifier 
HAVING count > 1;
 
-- Remove duplicates (keep most recent)
DELETE u1 FROM users u1
INNER JOIN users u2 
WHERE u1.id < u2.id AND u1.identifier = u2.identifier;
  1. Clear Character Data:
-- Remove specific player data
DELETE FROM users WHERE identifier = 'license:your_license_here';

Money/Account Issues

Error: "Money is not updating"

Solutions:

  1. Check Account Format:
-- Verify accounts are valid JSON
SELECT identifier, accounts, JSON_VALID(accounts) as is_valid 
FROM users 
WHERE JSON_VALID(accounts) = 0;
 
-- Fix invalid account data
UPDATE users 
SET accounts = '{"bank":5000,"black_money":0,"money":500}' 
WHERE JSON_VALID(accounts) = 0;
  1. Check Money Functions:
-- Server-side: Proper money handling
local xPlayer = ESX.GetPlayerFromId(source)
if xPlayer then
    local currentMoney = xPlayer.getMoney()
    xPlayer.addMoney(1000)
    print('Money before:', currentMoney, 'After:', xPlayer.getMoney())
end

Error: "Account type doesn't exist"

Solutions:

  1. Check Account Configuration:
-- In es_extended/config.lua
Config.Accounts = {
    bank = _U('account_bank'),
    black_money = _U('account_black_money'),
    money = _U('account_money')
}
  1. Add Missing Account Types:
-- If using custom accounts, ensure they're configured properly

Job Issues

Error: "Job doesn't exist"

Solutions:

  1. Verify Job in Database:
-- Check if job exists
SELECT * FROM jobs WHERE name = 'your_job_name';
 
-- Add missing job
INSERT INTO jobs (name, label) VALUES ('mechanic', 'Mechanic');
 
-- Add job grades
INSERT INTO job_grades (job_name, grade, name, label, salary, skin_male, skin_female) VALUES
('mechanic', 0, 'trainee', 'Trainee', 200, '{}', '{}'),
('mechanic', 1, 'mechanic', 'Mechanic', 400, '{}', '{}');
  1. Check Job Assignment:
-- Server-side: Proper job setting
local xPlayer = ESX.GetPlayerFromId(source)
if xPlayer then
    xPlayer.setJob('mechanic', 1)
end

Inventory Issues

Inventory Not Working

Error: "Items not showing"

Solutions:

  1. Check Inventory Table:
-- Verify user_inventory table exists
DESCRIBE user_inventory;
 
-- Check for data
SELECT * FROM user_inventory LIMIT 5;
  1. Verify Item Registration:
-- Check if items are properly registered in database
-- Items should be in the items table or configured in resources
  1. Check Weight System:
-- Ensure weight calculations work
local xPlayer = ESX.GetPlayerFromId(source)
local currentWeight = xPlayer.getWeight()
local maxWeight = xPlayer.maxWeight
print('Weight:', currentWeight, '/', maxWeight)

Error: "Cannot add item"

Solutions:

  1. Check Item Limits:
-- Verify item can be carried
local xPlayer = ESX.GetPlayerFromId(source)
if xPlayer.canCarryItem('bread', 5) then
    xPlayer.addInventoryItem('bread', 5)
else
    print('Cannot carry item - weight or space limit')
end
  1. Check Item Configuration:
-- Verify item exists in items table (if using database items)
SELECT * FROM items WHERE name = 'your_item_name';

Vehicle Issues

Error: "Vehicles not spawning"

Solutions:

  1. Check Vehicle Database:
-- Verify owned_vehicles table
DESCRIBE owned_vehicles;
 
-- Check vehicle data format
SELECT owner, plate, vehicle FROM owned_vehicles LIMIT 5;
  1. Check Vehicle Model:
-- Verify vehicle model exists
local model = GetHashKey('adder')
if not IsModelInCdimage(model) then
    print('Vehicle model not found')
    return
end
  1. Check Vehicle Ownership:
-- Verify player owns the vehicle
SELECT * FROM owned_vehicles WHERE owner = 'license:your_license_here';

Society/Job Issues

Society Not Working

Error: "Society account not found"

Solutions:

  1. Check Society Tables:
-- Verify society tables exist
SHOW TABLES LIKE 'addon_%';
 
-- Should show:
-- addon_account
-- addon_inventory
-- datastore
  1. Create Missing Society:
-- Create society account
INSERT INTO addon_account (name, label, shared) VALUES ('society_police', 'Police', 1);
INSERT INTO datastore (name, label, shared) VALUES ('society_police', 'Police', 1);
INSERT INTO addon_inventory (name, label, shared) VALUES ('society_police', 'Police', 1);
  1. Check Society Access:
-- Server-side: Access society account
TriggerEvent('esx_addonaccount:getSharedAccount', 'society_police', function(account)
    if account then
        print('Society balance:', account.money)
    else
        print('Society account not found')
    end
end)

Error: "Menu doesn't open"

Solutions:

  1. Check Menu Resources:
# Ensure menu resources are loaded
ensure esx_menu_default
ensure esx_menu_dialog
ensure esx_menu_list
  1. Check Menu Dependencies:
-- Verify menu system is available
if ESX.UI.Menu then
    -- Menu system loaded
else
    print('Menu system not available')
end
  1. Test Basic Menu:
-- Client-side: Test menu opening
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'test_menu', {
    title = 'Test Menu',
    align = 'top-left',
    elements = {
        {label = 'Option 1', value = 'option1'},
        {label = 'Option 2', value = 'option2'}
    }
}, function(data, menu)
    -- Handle selection
    menu.close()
end, function(data, menu)
    -- Handle close
    menu.close()
end)

Notification Issues

Error: "Notifications not showing"

Solutions:

  1. Check Notification Resource:
# Ensure notification resource is loaded
ensure esx_notify
  1. Test Notifications:
-- Client-side: Test notification
ESX.ShowNotification('Test message')
 
-- Server-side: Send to player
local xPlayer = ESX.GetPlayerFromId(source)
xPlayer.showNotification('Test message')

Performance Issues

High Resource Usage

Server Performance Problems

Symptoms:

  • High CPU usage
  • Lag/stuttering
  • Thread hitches

Solutions:

  1. Identify Resource Usage:
# In server console
resmon

# Look for resources using high CPU/memory
  1. Optimize Database Queries:
-- Bad: Multiple queries in loop
for i = 1, #players do
    MySQL.Async.fetchSingle('SELECT * FROM users WHERE identifier = @identifier', {
        ['@identifier'] = players[i]
    }, function(result)
        -- Process result
    end)
end
 
-- Good: Single query with IN clause
local identifiers = {}
for i = 1, #players do
    table.insert(identifiers, players[i])
end
 
MySQL.Async.fetchAll('SELECT * FROM users WHERE identifier IN (@identifiers)', {
    ['@identifiers'] = table.concat(identifiers, "','")
}, function(results)
    -- Process all results at once
end)
  1. Reduce Timer Frequency:
-- Bad: Too frequent
Citizen.CreateThread(function()
    while true do
        Citizen.Wait(0)  -- Runs every frame
        -- Heavy operation
    end
end)
 
-- Good: Reasonable frequency
Citizen.CreateThread(function()
    while true do
        Citizen.Wait(5000)  -- Runs every 5 seconds
        -- Heavy operation
    end
end)

Memory Leaks

Increasing Memory Usage

Solutions:

  1. Check for Event Listeners:
-- Ensure proper cleanup
AddEventHandler('onResourceStop', function(resourceName)
    if GetCurrentResourceName() == resourceName then
        -- Cleanup code here
        ESX = nil
    end
end)
  1. Clear Player References:
-- Clear player data on disconnect
AddEventHandler('esx:playerDropped', function(playerId, reason)
    -- Clear any stored player references
    if playerCache[playerId] then
        playerCache[playerId] = nil
    end
end)

Debug Techniques

Logging and Debugging

Enable Debug Mode

-- In config.lua
Config.Debug = true
 
-- Debug function
local function DebugPrint(...)
    if Config.Debug then
        print('^3[DEBUG]^7', ...)
    end
end
 
-- Usage
DebugPrint('Player data:', json.encode(ESX.GetPlayerData()))

Database Debugging

-- Check database operations
local function SafeQuery(query, parameters, callback)
    MySQL.Async.fetchAll(query, parameters, function(result)
        if result then
            callback(result)
        else
            print('^1[ERROR]^7 Database query failed:', query)
        end
    end)
end

Console Commands for Debugging

-- Server-side debug commands
RegisterCommand('esx-debug-player', function(source, args)
    local playerId = tonumber(args[1]) or source
    local xPlayer = ESX.GetPlayerFromId(playerId)
    
    if xPlayer then
        print('=== PLAYER DEBUG ===')
        print('Identifier:', xPlayer.identifier)
        print('Name:', xPlayer.getName())
        print('Job:', xPlayer.job.name, 'Grade:', xPlayer.job.grade)
        print('Money:', xPlayer.getMoney())
        print('Bank:', xPlayer.getAccount('bank').money)
    else
        print('Player not found')
    end
end, true)
 
RegisterCommand('esx-debug-db', function(source, args)
    local playerId = tonumber(args[1]) or source
    local xPlayer = ESX.GetPlayerFromId(playerId)
    
    if xPlayer then
        MySQL.Async.fetchSingle('SELECT * FROM users WHERE identifier = @identifier', {
            ['@identifier'] = xPlayer.identifier
        }, function(result)
            if result then
                print('=== DATABASE DEBUG ===')
                print('Raw data:', json.encode(result))
            end
        end)
    end
end, true)

Common Error Messages

Script Errors

ErrorCauseSolution
attempt to index a nil value (global 'ESX')ESX not initializedProper ESX initialization
attempt to call a nil value (method 'getMoney')xPlayer is nilCheck if player exists
bad argument #1 to 'pairs'Trying to iterate nil valueCheck if table exists
attempt to index a nil value (field 'job')Player data not loadedWait for esx:playerLoaded

Database Errors

ErrorCauseSolution
Table 'esx_server.users' doesn't existMissing database tablesImport SQL files
Column 'accounts' cannot be nullInvalid data insertionProvide default values
Duplicate entryTrying to insert duplicate keyUse proper constraints
Data too long for columnValue exceeds column lengthIncrease column size

Prevention Best Practices

Code Quality

  1. Always Check for Nil:
local xPlayer = ESX.GetPlayerFromId(source)
if not xPlayer then return end
  1. Use Error Handling:
MySQL.Async.fetchSingle('SELECT * FROM users WHERE identifier = @identifier', {
    ['@identifier'] = identifier
}, function(result)
    if result then
        -- Process result
    else
        print('No player found with identifier:', identifier)
    end
end)
  1. Validate Inputs:
RegisterNetEvent('myresource:server:action', function(data)
    local xPlayer = ESX.GetPlayerFromId(source)
    if not xPlayer then return end
    
    if not data or type(data) ~= 'table' then
        return
    end
    
    if not data.amount or type(data.amount) ~= 'number' or data.amount <= 0 then
        return
    end
    
    -- Process valid data
end)

Testing

  1. Test with Multiple Players
  2. Test Resource Restart
  3. Test Database Disconnection
  4. Test Invalid Inputs
  5. Monitor Resource Usage

Monitoring

  1. Regular Database Backups
  2. Monitor Server Performance
  3. Check Error Logs
  4. Update Dependencies

Regular maintenance and monitoring can prevent most issues before they become problems.

Always test changes on a development server before applying to production.