FiveM Job Scripts Troubleshooting FAQ: 12 Common Issues Fixed
Job scripts power everything from police shifts to mechanic paychecks β when they break, your server's economy stalls. This guide covers the 12 most common job script failures on ESX, QBCore, and QBox with step-by-step diagnostics and working code.

Job scripts power everything from police shifts to taxi rides to mechanic paychecks. When they break, your server's economy stalls β players can't earn, bosses can't manage, and the whole roleplay loop collapses. The failure surface spans database, framework integration, resource load order, and target zone alignment.
This guide covers the 12 most common job script failures across ESX, QBCore, and QBox. Each section includes the symptoms, the diagnostic steps, and working code you can drop in immediately.
Quick Diagnosis Table

Match the symptom you see to the most likely root cause before diving into individual sections.
| Symptom | Most likely cause | First thing to check |
|---|---|---|
| Clock-in does nothing | Job name mismatch or zone wrong | Print player job, teleport to zone coords |
| No paycheck received | Salary zero or pay timer not firing | Query job_grades, add debug print in pay loop |
| NPC not at work location | Invalid ped model or coordinates inside geometry | Test with s_m_m_doctor_01 at same coords |
| Job blip missing | Blip creation gated behind failing job check | Test blip with sprite 1 at known coords |
| Duty resets on reconnect | Duty state in memory only | Add DB write on duty toggle |
| Grade permissions wrong | Grade numbers mismatch between DB and config | Query job_grades, compare to config thresholds |
| Garage rejects player | Not on duty, or grade too low | Confirm duty status + grade in DB |
| Boss menu won't open | Grade too low or zone misaligned | Print player grade, verify zone coords |
| Society fund error | Missing addon_account_data entry | INSERT society account for that job |
| Two jobs conflicting | Duplicate job name in DB | Query jobs table for duplicates |
| Uniform not applying | Component IDs wrong for clothing system | Test each component ID manually |
| Whitelist blocking valid player | Identifier format mismatch | Print identifier used in check vs DB value |
1. Can't Clock In
The clock-in mechanic is the entry point for every job shift. When it fails, players can't work at all.
First, print the player's current job to confirm it matches the config exactly. Job names are case-sensitive β Police and police are different strings.
QBCore:
-- Run in F8 or add temporarily to a command
local PlayerData = QBCore.Functions.GetPlayerData()
print('Job: ' .. PlayerData.job.name)
print('Grade: ' .. tostring(PlayerData.job.grade.level))
ESX:
local xPlayer = ESX.GetPlayerData()
print('Job: ' .. xPlayer.job.name)
print('Grade: ' .. tostring(xPlayer.job.grade))
If the job name prints correctly, the issue is with the clock-in zone. Teleport to the exact coordinates in the config and confirm the zone is accessible β no walls, no other script's zone blocking it.
Fix checklist:
- Player's job name in database matches config exactly (check capitalisation)
- Job script starts after the framework in server.cfg
- Clock-in zone coordinates are accessible (teleport to verify)
- No target zone conflicts β check
/resmonfor zone registration errors - No F8 errors when approaching the zone
If using qb-target or ox_target, the zone will log a warning in F8 if its name conflicts with another script's zone. Search your F8 output for the zone name from your job config.
2. Job Not Paying
If players complete work but receive no salary, the payment chain is broken somewhere between the pay timer and the money addition.
Start by querying the salary configuration:
ESX:
-- Verify salary values in job_grades
SELECT grade, name, label, salary
FROM job_grades
WHERE job_name = 'police'
ORDER BY grade ASC;
QBCore:
QBCore defines salaries in qb-core/shared/jobs.lua rather than the database. Open that file and find your job:
['police'] = {
label = 'Law Enforcement',
defaultDuty = true,
offDutyPay = false,
grades = {
['0'] = { name = 'Recruit', payment = 50 },
['1'] = { name = 'Officer', payment = 75 },
['2'] = { name = 'Sergeant', payment = 100 },
['3'] = { name = 'Lieutenant', payment = 125 },
['4'] = { name = 'Chief', payment = 150 },
},
},
A missing grade entry or a payment = 0 silently produces no payout without any error. If salary values look correct, add a debug print inside the server-side pay interval loop to confirm it's actually running:
-- Server-side pay loop (inside your job resource)
CreateThread(function()
while true do
Wait(PayInterval * 60 * 1000) -- PayInterval is minutes
print('[DEBUG] Pay loop running, processing ' .. #GetPlayers() .. ' players')
for _, playerId in ipairs(GetPlayers()) do
-- your pay logic here
end
end
end)
If the debug print never appears, the loop stopped β likely due to a Lua error earlier in the file that was silently caught.
3. Job NPC Not Spawning
NPCs guide players through tasks at work locations. When they're missing, players have no interaction target.
Test the spawn at the same coordinates using a known-working ped model:
-- Run in F8 to test manually (client-side)
local model = GetHashKey('s_m_m_doctor_01')
RequestModel(model)
while not HasModelLoaded(model) do Wait(10) end
local ped = CreatePed(0, model, 123.4, -456.7, 28.5, 180.0, false, false)
SetBlockingOfNonTemporaryEvents(ped, true)
FreezeEntityPosition(ped, true)
SetEntityInvincible(ped, true)
If the test ped spawns but the script's NPC doesn't, the script's spawn is gated behind a condition that's failing β most commonly a job name check. Add a print inside the spawn function to confirm it executes.
If the test ped also fails, the coordinates are bad β inside geometry, underground, or in an interior that isn't loaded. Use the ~ key in-game coordinates (from your character's position) and add 1.0 to the Z value to avoid clipping into the ground.
Framework note: On QBCore, job NPCs are often defined in qb-jobname/client/main.lua inside a CreateThread that fires on resource start. If the job resource starts before the player data loads, the NPC spawn may check the player's job and find unemployed β add a framework-ready event listener instead.
4. Job Blip Not Showing
Blips mark job locations on the minimap. When they're missing, players can't find the worksite.
Blip creation can fail in two ways: the sprite ID is invalid, or the creation code never runs. Test with a known-working sprite at explicit coordinates:
-- Client-side test
local blip = AddBlipForCoord(-450.0, -340.0, 34.0)
SetBlipSprite(blip, 1) -- Sprite 1 always exists
SetBlipDisplay(blip, 4)
SetBlipScale(blip, 0.8)
SetBlipColour(blip, 2)
BeginTextCommandSetBlipName("STRING")
AddTextComponentString("Job Location Test")
EndTextCommandSetBlipName(blip)
If this test blip appears but the script's blip doesn't, the script's blip creation code isn't executing. The most common cause: blip creation is inside a job check that never passes, or the resource starts before the player's job data is available. Move blip creation to a PlayerLoaded event handler rather than a bare CreateThread.
Sprite IDs above 900 are from DLC and may not be available on all client versions. Use sprites in the 1β700 range for maximum compatibility.
5. Duty Status Not Saving
Most job scripts track duty status in server memory, which means it resets when a player disconnects. For persistent duty, the script must write to the database or framework metadata on every toggle.
QBCore β save duty to player metadata:
-- Server-side duty toggle handler
RegisterNetEvent('jobs:server:toggleDuty', function()
local src = source
local Player = QBCore.Functions.GetPlayer(src)
local currentDuty = Player.PlayerData.job.onduty
Player.Functions.SetJobDuty(not currentDuty)
-- QBCore's SetJobDuty writes to the players table automatically
end)
ESX β save duty to database column:
-- Server-side (requires a 'duty' column on the users table)
RegisterNetEvent('esx_jobs:setDuty')
AddEventHandler('esx_jobs:setDuty', function(state)
local src = source
local identifier = ESX.GetPlayerFromId(src).identifier
MySQL.update('UPDATE users SET duty = ? WHERE identifier = ?', {state and 1 or 0, identifier})
end)
If neither framework provides built-in duty persistence, add a column to the database and write on every toggle. Load it back on player connect.
6. Job Grade Permissions Wrong
Grades control access to armories, garages, boss menus, and management functions. A misconfigured grade threshold locks legitimate players out or unlocks actions for junior ranks.
Query the grade structure directly:
-- ESX: verify grade numbers and labels
SELECT grade, name, label, salary
FROM job_grades
WHERE job_name = 'police'
ORDER BY grade ASC;
Then compare against the permission check in config:
-- Common pattern: gate access by minimum grade
if PlayerData.job.grade.level >= Config.ArmoryGrade then
-- Open armory
end
If a player at grade 3 can't access content configured for grade 3, they're likely at grade 2 in the database. Query their actual grade:
SELECT job, job_grade FROM users WHERE identifier = 'char1:abc123';
QBCore note: job.grade.level is an integer. job.grade.name is the string label. Permission checks must use .level for numeric comparisons β using .name produces string comparison bugs that fail silently.
7. Job Vehicle Access Denied
Vehicle garages check job name, minimum grade, and usually duty status before spawning. If a player is rejected:
- Confirm they're on duty β most vehicle spawners check
ondutyfirst and return early if false - Verify their grade meets the vehicle's minimum grade in config
- Check that the spawn point isn't blocked by another vehicle
QBCore β debug vehicle access:
-- Add temporarily inside the vehicle spawn handler
local Player = QBCore.Functions.GetPlayer(source)
print('Job: ' .. Player.PlayerData.job.name)
print('Grade: ' .. tostring(Player.PlayerData.job.grade.level))
print('OnDuty: ' .. tostring(Player.PlayerData.job.onduty))
For custom vehicles, the spawn name must match exactly β including capitalisation. Default GTA vehicles always spawn; if they don't, the issue is in the spawner logic, not the model.
8. Boss Menu Not Opening
Boss menus give senior job members access to hiring, firing, and society fund management. They fail when the player's grade is too low or the target zone is misaligned.
Check the grade threshold in config:
-- Config.lua
Config.BossMenuGrade = 4 -- Player needs grade 4 or higher
-- In the target zone handler
if PlayerData.job.grade.level < Config.BossMenuGrade then
return -- silently exits
end
Print the player's grade to confirm it meets the threshold. If it does, the issue is the target zone. Boss menu zones drift when the script is installed on a server with a custom map or MLO at that location β re-position the zone to the correct world coordinates.
ESX: Boss menus are in esx_society. Verify the job is registered in esx_society/config.lua and that esx_society is ensured in server.cfg before the job resource.
QBCore: Boss menus are in qb-management. Each job needs an entry in its config with the matching job name string.
9. Society Fund Errors
Society funds are shared job bank accounts used for purchasing equipment and paying employees from a shared pool. If the fund errors or shows zero balance when it shouldn't:
ESX β check and create the society account:
-- Check if the society account exists
SELECT * FROM addon_account_data WHERE account_name = 'society_police';
-- If missing, create it
INSERT INTO addon_account_data (account_name, money, owner)
VALUES ('society_police', 0, NULL);
The account name must match the pattern society_<jobname> exactly. A typo like society_Police will create a separate account that the job script never finds.
QBCore: Society funds are managed through qb-management or qb-banking. The job must be listed in the management resource's config. Adding a new job requires a restart of the management resource.
Never modify society balances directly in SQL while the server is running β the in-memory state will conflict with the DB value until the next restart.
10. Multiple Jobs Conflicting
Job conflicts surface as silent failures β one job's script works, another doesn't β because they share an identical name in the database.
Find duplicates:
SELECT name, COUNT(*) as count
FROM jobs
GROUP BY name
HAVING count > 1;
Each job must have a unique name. If two scripts register the same name, the second one inherits the first's grade structure, which breaks permissions for both.
Multi-job systems (ESX): Resources like esx_multijob or ox_lib multi-job patches allow players to hold multiple jobs simultaneously. These patch the framework at a low level. If you're running one, ensure it's compatible with your specific ESX version β the patch points changed between esx-legacy 1.8 and 1.9.
11. Job Uniform Not Applying
Uniforms change the player's clothing components when they clock in or toggle duty. If nothing changes when the uniform should apply, the component IDs are wrong for your server's clothing setup.
Test each component individually:
-- Client-side: manually set a single component to test
local ped = PlayerPedId()
SetPedComponentVariation(ped, 4, 35, 0, 2) -- Component 4 = legs
-- Args: ped, componentId, drawableId, textureId, paletteId
Iterate through the drawableId values (0 to GetNumberOfPedDrawableVariations - 1) to find a valid clothing item. If the ID in your job config falls outside this range, the component silently applies nothing.
Clothing resource compatibility: Different systems store data in different formats. illenium-appearance and fivem-appearance use their own data structures β a uniform config built for esx_skin won't work directly with these systems and needs translation.
12. Job Whitelist Not Working
Whitelist jobs restrict interactions to players with that job assignment in the database. If the check blocks a player who should have access:
-- Check the player's job directly
SELECT job, job_grade FROM users WHERE identifier = 'char1:abc123';
-- For QBCore, check the players table
SELECT JSON_EXTRACT(job, '$.name') as job_name
FROM players
WHERE citizenid = 'ABC12345';
If the value is correct in the database but the whitelist still blocks, the script is comparing the wrong identifier format. Print the identifier the script sends to the whitelist check and compare it against what's stored.
Framework note on QBCore: QBCore uses citizenid as the primary player identifier, not identifier (which is Steam hex or license). A whitelist script that checks identifier against a QBCore players table will always fail because QBCore stores job data under citizenid.
Related Guides
- FiveM Economy Scripts Troubleshooting FAQ β Fix salary, tax, and society fund issues at the framework level
- FiveM Police Scripts Troubleshooting FAQ β Police-specific job failures: MDT, dispatch, armory
- FiveM Housing Scripts Troubleshooting FAQ β Job-owned properties and garage conflicts
Recommended Scripts
Need reliable, well-documented job scripts?
- FiveM Job Scripts β Premium and free job resources
- QBCore Job Scripts β QBCore-specific job resources
- Best FiveM Scripts 2026 β Top-rated scripts across all categories
Frequently Asked Questions
Why can't I clock in at my FiveM job?
Clock-in failures happen when the player's assigned job name doesn't match the config exactly (case-sensitive), the clock-in zone coordinates are wrong, or the script starts before the framework in server.cfg. Print the player's current job from the framework to confirm it matches, then teleport to the zone coordinates to verify they're accessible. Also check F8 for target zone errors β qb-target and ox_target both log zone registration failures.
Why isn't my FiveM job script paying players?
Payment failures break somewhere in the chain: the salary column is zero in the database, the pay interval timer never fires, or the money-add event errors silently. Query your job_grades table to verify salary values are non-zero for the grades your players hold. Add a debug print inside the server-side pay loop to confirm it's actually running. On QBCore, salary is in qb-core/shared/jobs.lua β a missing entry defaults to zero without an error.
Why aren't job NPCs spawning at work locations?
Job NPCs fail to spawn when the ped model hash is invalid, coordinates are inside geometry, or the spawn call never runs due to a job name mismatch. Test with a known-working model like s_m_m_doctor_01 at the same coordinates. If the manual spawn works but the script's NPC doesn't, the spawn is gated behind a job name check that's failing. Print the player's current job in the spawn function to verify.
Why isn't the job blip showing on the map?
Missing blips are caused by invalid blip sprite IDs, wrong coordinates, or blip creation code gated behind a job check that's failing. Test with sprite 1 at known coordinates. If the test blip appears, the script's blip code isn't running β usually because it's inside a job check that never passes. Check whether blip creation fires on resource start or only when the player's job is validated.
Why isn't duty status saving after disconnect?
Duty status is stored in memory by default and resets on disconnect. For persistence, the script must write to the database or framework metadata on duty toggle. On QBCore, use SetMetaData to write duty state. On ESX, write to a column in the users table. If your script doesn't support persistence, you need to add the database write yourself or switch to a script that handles it.
Why are job grade permissions not working correctly?
Grade permission issues come from mismatched grade numbers between the database and the script config. Grades start at 0 (lowest). Query your job_grades table and compare the grade numbers against the permission checks in config. A player at grade 2 who should access grade 3 content was either promoted incorrectly in the DB or the config threshold is set to 3 instead of 2.
Why can't players access job vehicles?
Vehicle access is gated by job name, grade, and usually duty status. If the garage rejects a valid player, confirm they're on duty (most vehicle access checks duty first), their grade meets the vehicle's minimum grade requirement in config, and the vehicle spawn point isn't physically blocked by another vehicle. Also check that the vehicle model name is spelled exactly as the spawn name β custom vehicles are case-sensitive.

