# Troubleshooting

Start with `dispatchcheck` in the server console and the startup banner. Most problems show up there. See [Installation](/new/dispatcher/installation).

## No access to the admin menu

**"You are not allowed to open the dispatch admin menu."**

- Your framework group must be in `Config.Admin.groups` (`admin`, `god`, `superadmin`), or you need the ACE `koja-dispatcher.admin`:

  ```cfg title="server.cfg"
  add_ace group.admin koja-dispatcher.admin allow
  add_principal identifier.license:xxxxxxxxxxxxxxxx group.admin
  ```

- `/dispatchadmin` only works in game. In the server console it prints a hint to use `dispatchcheck`.
- `dispatchtest`, `dispatchcheck` and `dispatchscan` in game also need the ACE for the command (`command.dispatchtest`...) and the `admin` or `god` group.

## The station does not start a shift

| Message or symptom | Cause and fix |
|---|---|
| No prompt or target option at the desk | The prompt and the target option only show for players whose job may dispatch. Check **Access & jobs** in `/dispatchadmin` and your grade. Access is checked when you load in, when your job or duty changes, and every 30 seconds, so after a change wait a moment. If your job is fine, the station is somewhere else than you think: use **Teleport** or **Set waypoint** on the Stations page. |
| "You are not a dispatcher." | Your job is not allowed to dispatch, or your grade is too low. `dispatchcheck` prints `[FAIL] dispatcher job "name" exists` when an allowed job does not exist in the framework. |
| "You must be at a dispatch console." | Stand within `Config.Dispatcher.consoleRange` (3 m) of the station. If the spot is off, move the station with **Adjust position** in the admin menu. |
| "Another dispatcher is already working at this station." | One dispatcher per station. Place another station. |
| "All dispatcher seats are taken." | `access.maxDispatchers` is reached. `0` means no limit. |
| "Another script blocked this action." or another message | A `shiftStart` hook of another resource refused the shift. When the hook returns a reason, that reason is passed on instead of `cancelled`. See [Hooks](/new/dispatcher/api/hooks). |
| "This is turned off on this server." with `/cad` | The remote CAD (`access.remote`) is off. |
| The shift ends right after it starts | You died, or moved more than `consoleRange + 4` m from the station. A station deleted in the admin menu or **End all shifts** also ends shifts. |
| No station anywhere | Stations are saved in the database once you change them in the admin menu; `Config.Consoles` is only the default. `dispatchcheck` shows `stations: 0`. |

## Grey or blank CAD

- `dispatchcheck` shows `web UI built` as `[FAIL]`: the `web/build` folder is missing. Use the release zip, or run `npm install` and `npm run build` in `web/`.
- The folder must be named `koja-dispatcher`, and `koja-lib` must be started before it.
- The CAD was minimised with Esc: press F7 at your station (or anywhere with the remote CAD) to bring it back.
- On a weak PC the 3D view can be heavy. Switch to the 2D map, or turn on **Performance mode (3D)** in the CAD settings.
- The CAD is in English although you set another language: the file in `Config.Locale` does not exist or is not valid JSON, so English is used. Check the file name and validate the JSON.
- After adding own files in `server/custom/` or `client/custom/`, check the console for Lua errors from them.

## No calls come in

Check the modes in the admin menu (the **Modes** page shows the flow).

| Symptom | Cause |
|---|---|
| No generated calls | Generated calls only come while a dispatcher is on shift, `mode.calls` is `generated` or `mixed`, and `Config.Calls.generate` is `true`. |
| Generated calls stop after a while | `calls.maxRinging` calls are ringing, or `calls.maxActiveIncidents` incidents are open. Close some, or raise the limits. |
| Player `/911` says nobody answers | Nobody is on shift and `mode.noDispatcher` is `reject`, or `mode.calls` is `generated`: real calls never ring at the console in that mode and follow the "nobody on shift" rule. |
| `/911` is ignored after the first message | Messages within `calls.playerReplyWindow` are added to the same call. `/911` is also rate limited per player. |
| No shots, crash or downed calls | Check the sensor switches on the Features page (`features.sensorShots`, `features.sensorCrash`, `features.sensorDowned`); they apply live. Sensors only create calls while a dispatcher can answer (someone on shift and `mode.calls` is not `generated`), and each has a chance and a cooldown per player. Shots from silenced weapons and from on-duty police, EMS and fire are ignored by default. |

Use `dispatchtest` or **Test call** on the Overview page to ring a call at once.

## Units are missing from the map

| Symptom | Cause |
|---|---|
| No AI units | `mode.units` is `players`, or `Config.AiUnits.enabled` is `false`, or the fleet editor has the units disabled. `dispatchcheck` shows the AI fleet count. |
| AI units of a department are off duty | `mode.units` is `mixed` and `units.disableWhenPlayers` players of that department are on duty. Set it to `0` to keep AI always. |
| A player is not shown as a unit | `mode.units` is `ai`; or the player's job is not in `Config.Departments[].jobs`; or they are off duty in the framework; or, with koja-mdt, off duty in the MDT; or they are on a dispatch shift (dispatchers are not units). Membership is checked every 5 seconds. |
| AI units drive in straight lines | The banner shows `Roads straight lines`: `data/roads.json` is missing or `Config.Roads.enabled` is `false`. |
| Civilians or NPC peds not shown | `features.showCivilians` and `features.showNpcPeds`, and the layer toggles in the map. |

## koja-mdt does not sync

- The **Integrations** page must show koja-mdt as **Running**, and `mdt.enabled` must be on. If it says **Not started**, ensure koja-mdt in `server.cfg`. The resource name must match `Config.Mdt.resource`.
- MDT calls reach the CAD only while a dispatcher is on shift and `mdt.pullCalls` is not `off`.
- Generated incidents are not sent to the MDT with `mdt.pushIncidents = 'real'`. Use `all` to send them too.
- Status and assignments need `mdt.syncStatus`. Only player units sync; AI units are CAD-only.
- `dispatchcheck` prints `[FAIL] koja-mdt integration` when it is installed and switched on but not started.

## Voice does not connect

- Only calls from real players use voice (`/911`, phone calls). Generated callers and text from other scripts stay text.
- `voice.enabled` must be on and `pma-voice` or `mumble-voip` started. With `voice.provider` forced to one of them, that one must run.
- pma-voice call channels must be enabled (`voice_enableCalls`, on by default in pma-voice).
- Calls use channels `voice.channelBase` to `channelBase + 899` (default `91100` to `91999`). If your phone script uses the same call channels, change `voice.channelBase`.
- Callers and units leave with `/hangup911`. The dispatcher ends the call from the voice dock.

## The phone does not ring at dispatch

- `phones.enabled` must be on.
- lb-phone: call or text the service number (`phones.number`, default `911`), or a company in `Config.Phones.companies`.
- npwd: call or text the service number. After changing the number, the new one is hooked within a few seconds.
- With `phones.resource` forced to a phone, only that phone's adapter is used, and it must be started.
- qs-smartphone-pro: the adapter listens to the job alert events; it is not verified in game. If nothing arrives, use the generic event.
- gksphone, yseries or any other phone: point its emergency number to `TriggerServerEvent('koja-dispatcher:phone:call', text)` (client) or `exports['koja-dispatcher']:PhoneCall(source, text)` (server). These work without a supported phone running; replies then arrive as normal notifications instead of phone messages.
- Calls from one player are limited to one every 2 seconds. The same modes as `/911` apply, so with nobody on shift the caller gets the "no dispatcher" message.

## Dispatchers are not paid

- `pay.enabled` must be on and the shift must last at least `pay.minimumMinutes`.
- The money goes to `Config.Pay.account` (`bank`). A shift ended by a disconnect is not paid.
- `pay.maximum` caps the payout; `0` means no cap.

## Version check

- Nothing printed: `Config.VersionCheck.enabled` is `false`, or koja-lib is not started. The check waits up to 20 seconds for koja-lib's report; with `Config.Debug` it prints when there is none.
- `Version check: no release information` means the version service has nothing for your version yet. `Development build` means your version is newer than the latest release.
- `Update available` and `CRITICAL UPDATE REQUIRED` print the latest version, up to three changelog lines and the download link.

## 3D terrain is flat

The release ships a scanned heightmap and building heights for the standard map, and the banner shows `3D terrain scanned`. If it shows `3D terrain flat (run /dispatchscan)`, the file `web/build/terrain/height.json` is missing: copy the `web/build/terrain` folder from the release again.

Scan again only after big map changes (large MLOs or map mods):

1. Join as an admin (admin group and the ACE `command.dispatchscan`).
2. Run `/dispatchscan`. Your client samples the height every `Config.Map3D.cellSize` metres over the whole map (a few seconds, progress on screen) and uploads it to the server (about 2 MB). Stay connected until you get the "3D map data saved" notification.
3. The server writes `height.json` and `height.b64` into `web/build/terrain/`. Restart the resource (`restart koja-dispatcher`) so the new files are served.

If the notification says "3D map data could not be saved", make sure the folder `web/build/terrain` exists in the resource and the server can write to it. `Config.Map3D.exaggeration` (also stored in `height.json`) scales the terrain height. The scan updates the terrain only; building heights are recalculated with a tool from the source repository, which is not part of the release.

## Discord logs are empty

- The convars must be set with `set` (not `setr`) and start with `https://`. A channel without its own URL uses `koja_dispatcher_webhook_default`.
- `discord.enabled` and the channel switch (`discord.shifts`, `discord.incidents`, `discord.calls`, `discord.admin`, `discord.events`) must be on. Incidents below `discord.minPriority` are not logged.
- A failed post prints `discord log (...) failed with status ...` in the console.

## Database errors

- `migration failed` at start: oxmysql cannot create the tables. Check the database user's rights and the error text.
- `[FAIL] settings loaded from the database`: the database was not ready, so the config defaults are used until it is.
- The banner shows `Database not ready` or `unreachable`: oxmysql is not started or cannot connect.
