legacy-idcard
Troubleshooting
Known gotchas and how to avoid them.
Troubleshooting
"Unknown card type" errors right after resource start
CardTypes is populated asynchronously by LoadCardTypes() in a CreateThread when server/server.lua starts. If another resource calls IssueCard or a player uses an item immediately on boot, the cache may still be empty. Add a small delay or retry logic when integrating at startup.
Card item exists in the admin panel but can't be used in-game
Creating a card type via /idadmin only writes to legacy_card_types — it does not create the matching inventory item. You must manually add an item definition with server.export = 'legacy-idcard.useCard' and consume = 0 to your inventory resource. The admin panel provides a copy button with the exact snippet.
Exports error as nil / resource fails to start
useCard, IssueCard, RevokeCard, HasCard, and GetPlayerCards all depend on server/init.lua having successfully wired up legacy-lib's Framework(), Inventory(), and GetLocale() exports. Make sure legacy-lib, ox_lib, and oxmysql are started before legacy-idcard in your server.cfg.
Issuing a card type again doesn't stack — it replaces the old one
IssueCard automatically invalidates (sets status = 'invalid') any existing 'valid' card of the same type for that player before issuing a new one, and syncs the old one to cityhall as invalid. This is by design, not a bug — card types are not stackable/additive.
Revoking an offline player's card doesn't remove the physical item
RevokeCard only touches inventory/licence metadata for players who are currently online (it scans GetPlayers()). For offline players, the DB row is deleted and cityhall is synced, but the physical item and licence flag remain until the player reconnects — there is no reconciliation-on-join logic in this resource.
SQL errors mentioning cityhall_documents or cityhall_doc_types
SyncToCityhall assumes a legacy-cityhall-compatible schema exists. This is a soft, optional dependency — if you don't run legacy-cityhall, these UPDATEs (not wrapped in pcall in server.lua, though the rename cascade in admin.lua is) will error. Either install legacy-cityhall or ignore these errors if you don't need the integration.
Renaming a card type breaks existing inventory items
legacy-idcard:updateCardType can rename a card type (data.originalName vs data.name), cascading the rename through legacy_player_cards.card_type and attempting to cascade into cityhall tables. It does not rename the corresponding item in your inventory system's items file — you must update that manually or the item name and card type name will desync.
Deleting a card type deletes players' cards, not just deactivates them
legacy_player_cards.card_type has FOREIGN KEY ... ON DELETE CASCADE to legacy_card_types.name. Deleting a card type (via /idadmin or legacy-idcard:deleteCardType) permanently deletes every player's card of that type — there's no soft-delete.
Re-running sql/schema.sql wipes all data
The schema script DROP TABLEs legacy_player_cards and legacy_card_types before recreating them. Never re-run it against a live database without a backup — you will lose all issued cards and any custom card types created through the admin panel.
Mugshots are missing / blank
legacy-idcard:captureMugshot returns nil if the MugShotBase64 resource isn't started. It's an optional, soft dependency not enforced by the manifest — install and start it if you want mugshots to appear on cards.
Admin panel NUI never opens / lookup panel stuck closed
The client tracks an isOpen flag shared by both the admin panel and the police lookup panel — if the NUI fails to send its close callback properly, the other panel can become permanently blocked until isOpen resets (e.g. resource restart). Check your web/dist build is up to date and the NUI is calling its close callback correctly.
/idadmin says "No permission" even for staff
Authorization is entirely server-side via Config.AdminGroups (ACE group group.<name>) OR the framework's admin flag. There is no client-side check — if staff are locked out, verify their ACE group matches an entry in Config.AdminGroups or that your framework correctly reports them as an admin.