legacy-lib
Troubleshooting
Known gotchas across the framework, banking, inventory, target, store, and config layers.
Troubleshooting
Startup order
- legacy-lib must start before any resource that depends on it. Other Legacy Scripts resources will error on exports or fail to start otherwise.
init.luamust load last within legacy-lib itself — it registers exports forFramework,Banking,Inventory,Store,Target, which must already exist as globals from earlier bridge files. If the manifest load order changes, exports can register asnil/empty tables.modules/framework/_default.luamust load first among framework files so later bridges only overwrite individual functions rather than leaving othersnil.
Banking
- Setting
LibConfig.Banking = 'auto'can let the weakerRenewed-Bankingbridge win the race againstlegacy-banking, becauselegacy-bankingalso self-provides asRenewed-Bankingin its own manifest. Onlylegacy-bankingfully supports IBANs andSocietyTransfer(needed bylegacy-bossmenu) — don't set this to'auto'or'Renewed-Banking'without checking dependents. qb-bankingandRenewed-Bankingbridges always return{ ok = false, reason = 'not_supported' }forSocietyTransfer, and blankiban/accountNumberfields forGetSocietyAccount— these are stubs, not real data.- Pinning
LibConfig.Bankingto an exact resource name that isn't actually running leavesBankingstuck on the no-op default, since no other bridge is allowed to bind. legacy-banking's bridge always starts afterlegacy-lib(it depends on it) — its bridge relies on theonResourceStartre-bind, not the initialGetResourceStatecheck, to bind correctly.
Framework
qbx_coresilently takes priority overqb-coreif both are running.qb-core.luaandqbx_core.luaboth callexports['qb-core']:GetCoreObject()— even the QBox bridge depends on theqb-coreresource/export existing.Framework.SetPlayerMetadataonly exists on theqb-core/qbx_corebridges — calling it under ESX or the default bridge will error as a nil field.- Account name translation ('cash' vs 'money') is implicit and framework-specific inside
Framework.GetAccountBalance/AddAccountBalance/RemoveAccountBalance— bypassingFramework.*and calling the underlying framework directly with the wrong literal name will look up the wrong balance. - Offline job-management paths (
SetEmployeeGrade,FireEmployee,GetEmployees) run raw SQL against framework-specific tables/columns with no schema validation — renamed/migrated tables will silently fail or return wrong data.
Inventory
- Only one inventory bridge binds;
ox_inventory's bridge file does not guard against overwriting an already-bound bridge, so ifox_inventoryis started it always wins regardless of load order. - If no supported inventory is running,
Inventory.AddItem/RemoveItem/RegisterUsableItemreturnfalse/log errors rather than throwing — check return values, don't assume success. - Metadata support differs per bridge: only
ox_inventoryreliably usesmetadatafor add/remove;qb-inventory/qs-inventoryignore it onRemoveItem. - Stash functions (
RegisterStash,SearchStash,AddStashItem,RemoveStashItem) only exist on theox_inventorybridge — calling them under any other bridge will error (not just no-op). Inventory.GetImagePathstrips a trailing.pngbefore rebuilding the path — pass the raw item name, not a pre-built path.
Client (Target / Inventory)
- Only the first detected
Target/inventory system wins if multiple are running — there is no priority logic beyond load order in the manifest. _default.luafallbacks forTarget/InventoryClientno-op with only a console warning — failures are silent in-game unless you're watching the console.GetLogo()returnsnilifLibConfig.LogoPathis unset/empty or the file can't be loaded — callers must handlenil.- Server-only exports (
GetLocale,SendWebhook,CheckVersion,Store) don't exist on the client — calling them client-side fails outright.
Store (JSON config)
Store.Read/Write/ReadOrMigrateare server-only (useLoadResourceFile/SaveResourceFile).- Always pass
GetCurrentResourceName()as theresourceargument — passing'legacy-lib'reads/writes inside legacy-lib's own folder instead of your resource's. Store.Readdistinguishes'absent'from'corrupt'— don't treat a corrupt file as empty and overwrite it;Store.Writeindependently refuses to clobber a file that exists but fails to parse.Storereturns plain tables, not an object — there's noStore:Load()-style API since metatables don't survive the FiveM export table-copy boundary. Mutations aren't auto-persisted; you must callStore.Writeexplicitly.- Only one backup (
<path>.bak) is kept, and only when the previous file both parses and differs from the new content — it is not a version history.
Webhooks / Version check
SendWebhooksilently no-ops ifurlisnil/''— no error, no log, so a blank webhook URL looks identical to a working no-op.CheckVersion/Lib.CheckVersionsilently does nothing ifLibConfig.VersionCheckURLis empty orresourceName/currentVersionare missing — enableLibConfig.Debugto see why a check isn't reporting.- The remote version JSON must be keyed exactly by the resource name being checked, or
CheckVersionreports "no version info found" rather than an error.
General
LibConfig.Debugmust betrueto see any[Legacy Lib] ...confirmation prints (module registration, bridge loaded, bind confirmations) — silence at startup does not necessarily mean failure.- Support for this resource is Discord-only (https://discord.gg/P2Jujr3Khh) — there is no issue tracker.