ox_lib + SwisserAI
The #1 complaint about AI in FiveM forums is hallucinated ox_lib functions. We scope generation to the real Overextended API surface — if a call would not exist, it does not get emitted.
The ox_lib hallucination problem
Spend ten minutes on forum.cfx.re and you will see the pattern: a developer pastes AI-written Lua, the server throws “attempt to call a nil value (field notify_player)” on load, and the thread turns into a 30-post debate about whether ox_lib “removed” a function. The function never existed.
ox_lib is exactly the kind of target that generic LLMs struggle with. It is popular enough to show up in training data under dozens of tutorials, versions, and blog posts — and small enough that the model never really nails which API is current versus which was renamed two releases ago. The result is confident code that calls invented methods.
SwisserAI treats ox_lib as a closed vocabulary. Generations are grounded on the current Overextended ox_lib API surface — lib.notify, lib.callback, lib.registerContext, the whole menu system, the cache helpers, the interface modules. When the model is tempted to invent, the grounding pushes it back to the real call. No “lib.createMenu.” No “ox_lib.notify_player.”
We frame this as an internal discipline rather than a magic guarantee — nothing catches every edge case, and the ox_lib repo ships changes. When in doubt, always review the diff before deploying.
ox_lib + Overextended coverage
lib.notify
Client and server notifications with type, duration, icon, and position. Correct argument shape, every time.
lib.callback
lib.callback.register on the server, lib.callback / lib.callback.await on the client. Awaited promises, no dangling cbs.
lib.registerContext
Context menus with nested submenus, onSelect handlers, arrow progression, and keepOpen flags.
lib.addRadialItem
Radial menu integration for ox_lib — with proper remove / clear lifecycle on resource stop.
ox_inventory integration
exports.ox_inventory:AddItem, CanCarryItem, RegisterStash, CreateDrop, usable-item hooks. Metadata-aware.
ox_target zones and models
addBoxZone, addSphereZone, addModel, addEntity. Proper option structures with distance, canInteract predicates, and icons.
ox_doorlock and state bags
Door registration, state-bag driven sync keys, and the correct auth guards for lock state changes.
oxmysql and ox_core helpers
exports.oxmysql:query_async with prepared placeholders, and ox_core helpers where your stack uses them.
ox_lib code that runs
Every snippet below is a real call against the current ox_lib API surface.
Client notification
lib.notify({
title = 'Crate delivered',
description = 'Earned $1,200 for the run.',
type = 'success',
position = 'top',
duration = 5000,
})Server callback + client await
-- server
lib.callback.register('swisser:server:priceQuote', function(source, itemName)
local prices = { radio = 2500, armor = 3000 }
return prices[itemName] or 0
end)
-- client
local price = lib.callback.await('swisser:server:priceQuote', false, 'radio')
lib.notify({ title = ('Radio costs $%s'):format(price), type = 'inform' })Context menu with submenu
lib.registerContext({
id = 'dealer_menu',
title = 'Dealer',
options = {
{
title = 'Buy',
description = 'Goods for sale',
icon = 'cart-shopping',
menu = 'dealer_buy',
},
{
title = 'Sell',
description = 'Turn stock into cash',
icon = 'hand-holding-dollar',
onSelect = function()
TriggerServerEvent('swisser:server:openSellScreen')
end,
},
},
})
lib.registerContext({
id = 'dealer_buy',
title = 'Buy',
menu = 'dealer_menu',
options = {
{ title = 'Radio $2,500', onSelect = function() TriggerServerEvent('swisser:server:buy', 'radio') end },
{ title = 'Armor $3,000', onSelect = function() TriggerServerEvent('swisser:server:buy', 'armor') end },
},
})
lib.showContext('dealer_menu')ox_target zone
exports.ox_target:addBoxZone({
coords = vec3(-47.4, -1757.3, 29.4),
size = vec3(1.5, 1.5, 2.0),
rotation = 0,
debug = false,
options = {
{
name = 'swisser:24_7_shop',
label = 'Open Shop',
icon = 'fa-solid fa-basket-shopping',
distance = 1.8,
onSelect = function()
TriggerEvent('swisser:client:open247')
end,
},
},
})ox_inventory stash
-- server: register once on resource start
exports.ox_inventory:RegisterStash('gang_stash_ballas', 'Ballas Stash', 50, 100000)
-- client: open it
TriggerServerEvent('ox_inventory:openInventory', 'stash', 'gang_stash_ballas')oxmysql prepared query
local rows = exports.oxmysql:query_await(
'SELECT id, label FROM swisser_items WHERE owner = ? LIMIT 50',
{ identifier }
)
for i = 1, #rows do
print(rows[i].id, rows[i].label)
endCommon hallucinations
If you have used any AI tool with ox_lib, these will look familiar.
Invented ox_lib functions
Generic AI cheerfully emits functions like ox_lib.notify_player or lib.createMenu that do not exist. SwisserAI generates against the current ox_lib surface — if a call would hallucinate, it does not get written.
Legacy wrapper patterns
Early ox_lib had a lib = exports.ox_lib:load() style bootstrap in some builds. Modern ox_lib injects the lib global. Code that mixes the two silently breaks.
Wrong lib.callback direction
lib.callback.register is server-side only; on the client you use lib.callback / lib.callback.await. AI that flips these ships broken callbacks.
ox_target option drift
The option shape evolved (distance, canInteract, onSelect replaced older action). Snippets that mix versions throw silently on hover.
Frequently Asked Questions
ox_lib & oxmysql: the calls you came for
Exact, copy-paste signatures for the most-searched Overextended functions — verified against the current ox_lib and oxmysql source, not guessed.
lib.callback.register(name, cb)
Register a server-side callback the client can await.
-- server
lib.callback.register('myresource:getBalance', function(source, account)
return GetPlayerBalance(source, account)
end)
-- client: await the result (2nd arg is a delay or false, NOT a player id)
local money = lib.callback.await('myresource:getBalance', false, 'bank')On the client the 2nd argument is a delay (or false). On the server, lib.callback.await(event, playerId, ...) takes the player id there. The two signatures are not the same.
lib.registerContext(context) + lib.showContext(id)
Build a context menu. Registering it does not open it.
lib.registerContext({
id = 'garage_menu',
title = 'Garage',
options = {
{ title = 'Spawn vehicle', icon = 'car', onSelect = function() end },
},
})
-- open it separately:
lib.showContext('garage_menu')Common mistake: calling lib.registerContext and expecting the menu to appear. You must call lib.showContext(id) afterwards.
lib.skillCheck(difficulty, inputs)
Blocking skill check; returns true on success, false on fail.
local success = lib.skillCheck({ 'easy', 'easy', 'hard' }, { 'w', 'a', 's', 'd' })
if success then
-- player passed
endlib.notify(data) / lib.notify(playerId, data)
Show a notification. Client takes one table; server takes a player id first.
-- client
lib.notify({ title = 'Saved', description = 'Vehicle stored', type = 'success' })
-- server (notify one player)
lib.notify(source, { description = 'Welcome back', type = 'inform' })MySQL.single.await / MySQL.scalar.await (oxmysql)
Awaitable oxmysql queries — one row, one value, many rows, or an insert id.
local row = MySQL.single.await('SELECT * FROM users WHERE identifier = ?', { identifier })
local name = MySQL.scalar.await('SELECT name FROM users WHERE id = ?', { id })
local rows = MySQL.query.await('SELECT * FROM vehicles WHERE owner = ?', { owner })
local insertId = MySQL.insert.await('INSERT INTO logs (msg) VALUES (?)', { msg })Modern methods are single, scalar, query, insert, update, prepare (each with .await). The old fetchSingle / fetchScalar / fetchAll / execute aliases still work but are legacy.
AI for other FiveM frameworks
SwisserAI generates idiomatic code for every major FiveM framework. Explore the others:
Stop debugging invented ox_lib calls
250 free credits. Generate, review, ship. ox_lib code grounded on the real API.