# 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

:::accordion{title="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.
:::

:::accordion{title="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.
:::

:::accordion{title="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](/new/mdt/installation), or set `requireItem` to `false`.
:::

:::accordion{title="The key or command does nothing"}
- Dead players cannot open it unless `Config.Open.allowWhenDead` is `true`.
- With `Config.Open.vehicleOnly` it only opens in an emergency class vehicle.
- It does not open while the pause menu is active.
- `Config.Open.command` or `Config.Open.keybind` may be empty.
- The key can be rebound by the player in the FiveM key bindings. Another resource may use the same key.
:::

:::accordion{title="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](/new/mdt/departments-and-permissions). Pages without permission are hidden. EMS and fire levels do not include `citizens.view` or `incidents.view` by default.
:::

:::accordion{title="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

:::accordion{title="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 `oxmysql` and `koja-lib`.
- If the resource is not listed as started, check the order in `server.cfg`: `oxmysql`, framework, `koja-lib`, then `koja-mdt`.
:::

:::accordion{title="The interface is in the wrong language"}
See [Translations](/new/mdt/translations). Players can have their own language set in Settings. `Config.Locale` is only the default.
:::

:::accordion{title="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](/new/mdt/integrations). Also make sure your characters and vehicles live in the standard framework tables.
:::

## Database

:::accordion{title="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.
:::

:::accordion{title="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

:::accordion{title="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.
:::

:::accordion{title="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.
:::

:::accordion{title="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.
:::

:::accordion{title="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

:::accordion{title="/mdtweb says the portal is disabled"}
`Config.Web.enabled` is `false`.
:::

:::accordion{title="/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 in `Config.Web.relay.url`.
- Set `Config.Web.publicUrl` to your own address.
- Make sure the `web_baseUrl` convar is set (it is normally set by FXServer when the server has a license key).
:::

:::accordion{title="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/`, set `Config.Web.publicUrl` and `Config.Web.trustProxyHeaders = true`.
- If you renamed the resource folder, the path changes with it.
- Cross-origin setups need the origin in `Config.Web.allowedOrigins`.
:::

:::accordion{title="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.
:::

:::accordion{title="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

:::accordion{title="The dispatcher voice does not play"}
- `Config.DispatchVoice.enabled` must 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.language` uses speech synthesis if the browser has a voice for it, otherwise only radio beeps.
- Announcements older than `staleSeconds` are skipped, and the queue is limited by `maxQueue`.
:::

:::accordion{title="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.
:::

:::accordion{title="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.
:::

:::accordion{title="Gunshots do not create a call"}
- On-duty police do not trigger alerts by default (`Config.Dispatch.gunshots.ignoreOnDuty` with `ignoreJobs`). Test with a civilian job or turn the option off.
- Silenced weapons (`ignoreSilenced`), the weapons in `ignoreWeapons` and the places in `ignoreZones` (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.enabled` and `Config.Dispatch.gunshots.enabled` must both be on.
:::

## Sentencing, jail, fines

:::accordion{title="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`.
:::

:::accordion{title="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](/new/mdt/integrations).
:::

:::accordion{title="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.
:::

:::accordion{title="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

:::accordion{title="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](/new/mdt/installation). Citations need metadata support; without it the latest citation of the player is shown.
:::

:::accordion{title="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

:::accordion{title="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`.
:::

:::accordion{title="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

:::accordion{title="/mdtdebug says no permission"}
The player needs one of the aces in `Config.DebugTools.aces`, see [Demo mode](/new/mdt/demo-mode). If `Config.DebugTools.enabled` is `false`, the command does not exist.
:::

:::accordion{title="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`.
