> For the complete documentation index, see [llms.txt](https://tyrix.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tyrix.gitbook.io/docs/script-resources/tyrix-droply/troubleshooting.md).

# Troubleshooting

<details>

<summary>The app doesn't appear on the phone</summary>

Check the server console for:

```
[Droply] Could not add app: <reason>
```

Droply waits for lb-phone to report `started` before registering, and re-registers on an lb-phone restart. If the app is still missing:

* Confirm `lb-phone` is started **before** `tyrix_droply`.
* Confirm `Config.AppIdentifier` does not collide with another custom app.
* Restart `tyrix_droply` after lb-phone is fully up.

</details>

<details>

<summary>A business doesn't appear in the app</summary>

Stores are hidden when they have **no priced, visible items** — there would be nothing to show. Walk the checklist:

1. Has an admin assigned at least one crafting station? (Business Manager → Droply tab)
2. Does that station actually produce recipe outputs?
3. Has the boss set a price above `0` on at least one item?
4. Is that item hidden by the eye toggle?
5. Is the store toggled **Open**?

Closed stores *do* appear, greyed out. A store missing entirely means the menu projection is empty — start at step 1.

</details>

<details>

<summary>The boss sees an empty menu</summary>

The owner menu lists everything the assigned stations can produce, including unpriced items. If it is empty, the station assignment is the problem, not the pricing. Re-check the Droply tab in the Business Manager and confirm the station's `jobs` map includes that business.

</details>

<details>

<summary>Customers can't place an order</summary>

Each of these blocks an order with its own notification:

| Message                                           | Cause                                                                          |
| ------------------------------------------------- | ------------------------------------------------------------------------------ |
| "Sign up in Droply before placing an order."      | No customer account yet. The app pushes them to signup.                        |
| "Store is closed."                                | `is_open` is `0`.                                                              |
| "This business is not accepting orders from you." | The customer is on that store's blocklist.                                     |
| "You already have an active order."               | `oneActiveOrderPerCustomer` is on and they have one live.                      |
| "Insufficient funds. Need $X."                    | Total exceeds the sum of their `paymentSources` balances.                      |
| "Invalid order: …"                                | Cart failed sanitisation — off-menu item, or a quantity/count over the limits. |

</details>

<details>

<summary>The driver can't go "Out for Delivery"</summary>

The driver must hold the **complete** order, either loose in their inventory or packed into a **single** `business_box`. The notification names the first missing item and the quantity short.

Common causes:

* Items split across **two** boxes — one box must cover the whole order.
* Items in a box that is missing one line, so the box does not qualify and the loose count is also short.
* Ordered items were consumed or dropped after being crafted.

</details>

<details>

<summary>The handover option doesn't appear</summary>

The third-eye option only shows when **all** of these hold:

* The driver has at least one `enroute` order.
* The targeted ped is a **player**.
* That player is the customer on one of the driver's enroute orders.
* The driver is within `handoverInteractDistance` (default 2.5 m).

</details>

<details>

<summary>"Not close enough to the customer"</summary>

The server measures against the customer's **live ped**, not the recorded drop-off, with a limit of `handoverDistance + 2.0` m. If the customer walked off after the pin was placed, the driver must follow them. These rejections are logged to the `exploit` channel with the measured distance — a few from ordinary latency are normal; a pattern from one player is not.

</details>

<details>

<summary>Delivery fails at the last moment</summary>

| Message                                    | Cause                                                                               |
| ------------------------------------------ | ----------------------------------------------------------------------------------- |
| "Customer is offline."                     | They disconnected. The order auto-cancels after `offlineGraceSec`.                  |
| "Customer's inventory is full."            | Capacity check failed. Nothing moved; the order stays `enroute`.                    |
| "Customer can no longer pay."              | Their balance dropped below the total since ordering. The order cancels as `funds`. |
| "Customer payment failed; items returned." | The charge failed after transfer. Items were rolled back to the driver.             |

None of these lose items or money — transfers roll back and payment is charged last.

</details>

<details>

<summary>The customer sees no driver blip</summary>

The driver's client only reports position after moving `workerMoveGateMeters` (default 15 m), though a newly-`enroute` order forces one immediate report. If the blip never appears:

* Confirm the order actually reached `enroute` — `preparing` does not track.
* Confirm the customer is online; relays are dropped when the customer's source is unresolved.

</details>

<details>

<summary>Promo code rejected</summary>

| Reason              | Fix                                                                                        |
| ------------------- | ------------------------------------------------------------------------------------------ |
| Invalid / not found | Codes are per business and uppercase-normalised. Check the customer is at the right store. |
| Expired             | Past `expires_at`. A bare date expires at end of day.                                      |
| Fully redeemed      | `uses` hit `max_uses`.                                                                     |
| Already used        | `once_per_customer` is on and they have a redemption row. Cancelled orders release theirs. |

If codes are hitting "fully redeemed" far too early, you are on a build predating the counter fix — restart the resource once and the counters are recomputed. See [Promotions → Counter repair](/docs/script-resources/tyrix-droply/promotions.md#counter-repair).

</details>

<details>

<summary>Orders vanished after a restart</summary>

Expected. Live order state cannot survive a restart, so unfinished orders are cancelled cleanly on start:

```
[Droply] reconciled N orphaned order(s) after restart
```

No customer is charged, since money is only captured at handover.

</details>

<details>

<summary>A customer is permanently stuck on "active order"</summary>

This should not happen — the gate self-heals when the recorded slot points at a missing or terminal order. If it persists, confirm the order row is genuinely terminal:

```sql
SELECT id, status FROM tyrix_droply_orders WHERE customer_id = '<identifier>' ORDER BY id DESC LIMIT 5;
```

Restarting the resource cancels any non-terminal row and frees the slot.

</details>

<details>

<summary>Enabling debug output</summary>

```lua
Config.Debug = true
```

Prints cache population counts, worker focus changes, order assignment traces, and app registration confirmation. Turn it back off in production.

</details>
