Troubleshooting
Turn on Config.Debug = true and restart the resource to get extra lines in the server console. Check the F8 console for client errors.
Opening the MDT
You don't have access to the MDT.
The player's framework job is not listed in any department. Open shared/departments.lua and add the job name to the jobs list of the right department. Job names are case sensitive and must match the framework job exactly. A job can belong to only one department. Restart the resource after the change.
Go on duty to use the MDT.
Config.Open.allowOffDuty is false. Either set it to true, or have the player go on duty first (/mdtduty or the dashboard toggle). With Config.Duty.syncWithFramework on, the framework job duty is changed as well.
You need a tablet to open the MDT.
Config.Open.requireItem is true and the player has no Config.Open.item (default mdt_tablet). Add the item to your inventory resource, see Installation, or set requireItem to false.
The key or command does nothing
- Dead players cannot open it unless
Config.Open.allowWhenDeadistrue. - With
Config.Open.vehicleOnlyit only opens in an emergency class vehicle. - It does not open while the pause menu is active.
Config.Open.commandorConfig.Open.keybindmay be empty.- The key can be rebound by the player in the FiveM key bindings. Another resource may use the same key.
A page is missing or shows no permission
The player's access level does not include the permission for that page. See the permission list in Departments and permissions. Pages without permission are hidden. EMS and fire levels do not include citizens.view or incidents.view by default.
Suspended officer
A member can be suspended from their profile by ranks with roster.suspend. A suspended member is limited in the MDT. The same rank can reinstate them.
Blank or broken interface
The tablet opens but is blank or grey
- Check that you installed the release zip, which contains
web/build/index.html. A plain source checkout without the built interface shows nothing. - Restart the resource and the client (the NUI cache can hold an old build).
- Look at the F8 console for NUI errors and at the server console for a failed start. The resource will not start without
oxmysqlandkoja-lib. - If the resource is not listed as started, check the order in
server.cfg:oxmysql, framework,koja-lib, thenkoja-mdt.
The interface is in the wrong language
See Translations. Players can have their own language set in Settings. Config.Locale is only the default.
Lists are empty (no people or vehicles)
The framework bridge returns nothing. Check the console banner for the detected framework. If koja-lib reports a framework that is not esx or qb, you need to fill in the Custom functions in editable/server/framework.lua, see Integrations. Also make sure your characters and vehicles live in the standard framework tables.
Database
migration statement failed in the console
A statement of koja-mdt.sql failed. Check that oxmysql is connected and the database user can create tables and alter tables. The message includes the SQL error. Statements are CREATE TABLE IF NOT EXISTS, so you can fix the cause and restart.
Admin panel job editing is missing
Job editing only works on ESX and needs the jobs and job_grades tables. On other frameworks edit jobs in your framework.
Photos
No Take a photo button
The button needs all of these: Config.Camera.enabled, a non-empty Config.Camera.uploadUrl and the screenshot-basic resource started. You can always paste an image link by hand.
Selfie does not work
The selfie needs screenshot-basic. Without it the server says it needs the resource. Profile pictures taken with /mdtphoto use the game's own head shot and work without it.
Upload succeeds but no image appears
The upload response is searched for a link in these places: Discord attachments, files[0].url, data.url, url and link. Use an endpoint that returns one of these, and check Config.Camera.uploadField matches what the endpoint expects.
Selfie is rejected
The server console prints selfie from X rejected: <reason>, where X is the player id. The reason is too_large when the image is over the size limit and invalid when it is not a valid webp, png or jpeg data URL. If the picture is too big, raise Config.Avatars.selfie.maxLength or lower Config.Avatars.selfie.size and Config.Avatars.selfie.quality. A selfie taken before Config.Avatars.selfie.cooldownSeconds has passed shows a cooldown notification instead.
Web portal
/mdtweb says the portal is disabled
Config.Web.enabled is false.
/mdtweb gives a code but no address
The message says to ask the staff for the web address when no URL can be built. Fix it in one of these ways:
- Leave
Config.Web.relay.enabled = true. The server must be able to open an outgoing HTTPS connection (port 443) to the relay address inConfig.Web.relay.url. - Set
Config.Web.publicUrlto your own address. - Make sure the
web_baseUrlconvar is set (it is normally set by FXServer when the server has a license key).
The address does not open
- Direct access uses the FXServer HTTP port, normally
30120, at the path/koja-mdt/(the resource name). The TCP port must be open in your firewall and forwarded on your router. - Behind a reverse proxy, point it to
http://127.0.0.1:30120/koja-mdt/, setConfig.Web.publicUrlandConfig.Web.trustProxyHeaders = true. - If you renamed the resource folder, the path changes with it.
- Cross-origin setups need the origin in
Config.Web.allowedOrigins.
The code is rejected
Codes are single use and valid for Config.Web.loginCodeMinutes minutes (default 10). Request a new one with /mdtweb. There is a 10 second cooldown between requests. The player needs a department job unless Config.Web.allowCitizens is on.
Actions say you are not in game
Responding to calls, sentencing, hiring a nearby player and the duty toggle need a character in the game. They are not available from the web portal while the player is offline.
Dispatch and voice
The dispatcher voice does not play
Config.DispatchVoice.enabledmust be on, and the officer must be on duty.- The officer can switch it off in Settings. Use the Test button there.
- Calls are only read up to the priority threshold in
Config.DispatchVoice.minPriority. - Individual announcement types can be off: see
Config.DispatchVoice.announce(closed calls are off by default). - Voice clips exist for English and Polish. Another
Config.DispatchVoice.languageuses speech synthesis if the browser has a voice for it, otherwise only radio beeps. - Announcements older than
staleSecondsare skipped, and the queue is limited bymaxQueue.
Players do not receive a call
Calls only go to departments that match the call. A call without a target goes to law enforcement. EMS and fire departments need the call to target them (for example with the jobs or departments fields of the CreateDispatch export). Non-field departments (field = false) never receive calls. /911 and /311 need a message after the command and have a cooldown of Config.Dispatch.publicCooldown seconds.
The panic key does nothing
Config.Dispatch.panicKey may be empty, the cooldown (panicCooldown) may still be running, or the player has no MDT department.
Gunshots do not create a call
- On-duty police do not trigger alerts by default (
Config.Dispatch.gunshots.ignoreOnDutywithignoreJobs). Test with a civilian job or turn the option off. - Silenced weapons (
ignoreSilenced), the weapons inignoreWeaponsand the places inignoreZones(gun ranges) are ignored. - The alert is delayed by 3 to 8 seconds (
reportDelay), so the call is not instant. - A player triggers at most one alert per
cooldownSeconds(30), and shots close to a recent alert (mergeRadius,mergeSeconds) are merged into it. Config.Dispatch.enabledandConfig.Dispatch.gunshots.enabledmust both be on.
Sentencing, jail, fines
The fine is not taken
Integrations.Fine takes money from Config.Processing.fineAccount and falls back to cash. If both are too low, processing fails unless Config.Processing.allowNegativeBalance is on. By default the suspect must be online and within Config.Processing.maxDistance.
The player is not jailed
Check Config.Processing.jailSystem. With 'auto' the first supported jail resource that is started is used. If none is, the MDT only fires koja-mdt:server:jailPlayer, which you can handle in your own script. Add your own jail script as described in Integrations.
The jail record stays open or closes too early
Config.Custody.secondsPerMonth should match your jail script. If the script reports its own releases, add it to Config.Custody.trackedSystems so the clock does not close the record.
Impound does not move the vehicle
The garage fallback needs the framework columns owned_vehicles.stored (ESX) or player_vehicles.state (QB). Otherwise the MDT only records the impound and sets the flag. Set Config.Custody.impoundSystem to your resource, or add an adapter.
Items
Using the citation or document does nothing
The item must exist in your inventory with the exact name from Config.Citations.item or Config.Forms.item, and be usable. For ox_inventory the item needs server = { export = 'koja-lib.useItem' }, see Installation. Citations need metadata support; without it the latest citation of the player is shown.
You don't have a body camera
Config.Bodycams.requireItem is on and the officer has no bodycam item. Add the item or turn the option off.
ANPR and cameras
The ALPR panel does not show
The vehicle must be one of Config.Anpr.vehicle.models or an emergency class vehicle (allowEmergencyClass). The officer must be on duty (Config.Anpr.requireDuty) and have a law enforcement department. The default toggle key is F10.
I cannot place CCTV cameras
Camera placement requires the koja-mdt.cameras ace (Config.Cameras.adminAce) and the MDT officer must not be suspended.
Demo and admin
/mdtdebug says no permission
The player needs one of the aces in Config.DebugTools.aces, see Demo mode. If Config.DebugTools.enabled is false, the command does not exist.
The Admin panel is not visible
Give the player the koja-mdt.admin ace, or a rank with admin.view or admin.manage. Remember that command only works when Config.Admin.allowCommandAce is true.
Players are kicked
If Config.Security.dropAfterFlags is above 0, players who repeatedly send forbidden or flooding requests are removed with Config.Security.dropMessage. The default 0 never kicks. A legitimate player hitting a rate limit just sees the request rejected for a moment.
Still stuck
Collect the server console output at start and any client F8 errors, plus your Config.Debug output, and contact support with the resource version from fxmanifest.lua.