# Error codes

> Every error code from the Lia SDK and the server API, what it means and what to do. From the Lia developer docs: https://docs.lialabs.org/reference/errors

## In your page

SDK calls reject with an `Error` that has a `code`. Messages are in English for you, not for players: show your own words.

### Anywhere

| Code | Meaning | What to do |
| --- | --- | --- |
| `not_in_lia` | The page isn't open inside the Lia app. | Show "Open this in Lia". |
| `not_ready` | Lia hasn't finished opening your app, or `Lia.inviteLink` was called before `Lia.init()`. | Call `Lia.init()` first, then try again. |
| `timeout` | Lia didn't answer within 20 seconds. | Try again. |
| `slow_down` | More than 20 calls in one second. | Call less often (don't call the SDK every frame). |
| `busy` | Another of Lia's screens (pay sheet, ad, dialog, picker, scanner, Face ID) is open. | Wait for the first to finish. |
| `unknown_method` | Lia doesn't know this call. An older Lia app, or a typo. | Check `Lia.isVersionAtLeast`. |
| `unsupported` | This phone can't (no sensor, no Face ID, clipboard unavailable). | Offer another way. |
| `failed` | Something went wrong in Lia. | Try again. |
| `network` | No connection. | Try again. |
| `not_open` | Lia has no player for this person in your app yet. | Call `Lia.init()` first. |
| `not_found` | Lia couldn't find your app for this person (paused, or no longer open to them). | Close the app. |

### Payments, ads and invites

| Code | From | Meaning |
| --- | --- | --- |
| `cancelled` | `Lia.pay` | The person closed the pay sheet without paying. |
| `no_product` | `Lia.pay` | No active product with that id. |
| `insufficient_balance` | `Lia.pay` | Not enough $LIA. Lia's sheet shows this itself and stays open, so your page usually hears `cancelled` instead. |
| `bad_format` | `Lia.showAd` | Not `rewarded`, `rewardedInterstitial` or `interstitial`. |
| `notifications_off` | `Lia.requestNotifications` | Your app may not send notifications. |
| `bad_start_param` | `Lia.inviteFriend`, `Lia.inviteLink` | Not 1 to 64 of letters, digits, `_` and `-`. |
| `bad_stake`, `bad_cursor` | `Lia.lobby.tables` | A stake that isn't a number of LIA, or a cursor that isn't the `next` from before. |
| `bad_reason`, `bad_note`, `bad_tableId` | `Lia.lobby.report` | A reason other than name, unfair, abuse or other; a note over 300 characters. |
| `bad_session` | `Lia.lobby` | Lia couldn't renew the session. Try again. |
| `slow_down` | `Lia.lobby.report` | More than 20 reports in a day. |
| `bad_idempotency_key` | `Lia.pay`, `Lia.inviteFriend`, `Lia.submitScore`, cloud storage writes | Not 1 to 64 of letters, digits, `_`, `-`, `.` and `:`. |

Ad results that aren't a reward (`cancelled`, `skipped`, `no_ad`, `too_soon`) are answers, not errors: see [Ads](/concepts/ads#what-you-get-back).

### Storage, scores, the phone

| Code | From | Meaning |
| --- | --- | --- |
| `storage_full` | Storage `setItem` | Cloud: 1024 keys. Device: 5 MB. Secure: 10 items. |
| `bad_key`, `bad_keys`, `bad_value`, `bad_item` | Storage | A key or value that doesn't fit. |
| `bad_score` | `Lia.submitScore` | Not a whole number from 0 to 1,000,000,000,000. |
| `bad_refresh_rate` | Sensors' `start` | Not 20 to 1000 ms. |
| `unknown_screen` | `Lia.openLiaLink` | Not one of Lia's screens. |
| `bad_url` | `Lia.openLink` | Not an https address up to 2048 characters. |

### Arguments

The SDK checks arguments before anything reaches Lia, and rejects (or throws) with `bad_` and the argument's name: `bad_productId`, `bad_text`, `bad_title`, `bad_body`, `bad_confirm`, `bad_message`, `bad_buttons`, `bad_button_type`, `bad_id`, `bad_position`, `bad_color`, `bad_text_color`, `bad_screen`, `bad_reason`, `bad_listener`. Each method's page says which it can give.

## From the server API

Errors answer with a 4xx status and `{ "error": "<code>" }`, sometimes with more fields.

| Status | Code | From | Meaning |
| --- | --- | --- | --- |
| 401 | `bad_key` | Every endpoint | No key, an unknown key, a live key while your app isn't live, or your app is paused. |
| 400 | `bad_amount` | `POST /rewards` | Not a number above 0 and up to 10,000. |
| 400 | `bad_key_field` | `POST /rewards` | The reward's `key` (or, without one, the `Idempotency-Key` header) is missing or not 1 to 128 of letters, digits, `_`, `.`, `:` and `-`. |
| 400 | `bad_idempotency_key` | Any `POST` | The `Idempotency-Key` header isn't 1 to 128 of letters, digits, `_`, `-`, `.` and `:`. |
| 409 | `idempotency_key_in_use` | Any `POST` | A request with this key is still running. Wait a second (`Retry-After: 1`) and send it again. |
| 422 | `idempotency_key_reused` | Any `POST` | This key was used with a different body. Use a new key for a new request. |
| 400 | `bad_text` | `POST /notifications` | Empty, over 180 characters, or has a `problem`: `link`, `phone` or `scam`. |
| 400 | `bad_start_param` | `POST /notifications` | Not 1 to 64 of letters, digits, `_` and `-`. |
| 403 | `notifications_off` | `POST /notifications` | Your app may not send notifications. |
| 403 | `not_allowed` | `POST /notifications` | The player hasn't allowed your notifications. |
| 404 | `not_found` | Charges, `POST /ads/verify` | Nothing with that id in your key's mode. |
| 404 | `unknown_player` | `POST /rewards`, `POST /notifications` | No such player (in your key's mode). |
| 409 | `refunded` | `POST /charges/:id/deliver` | The charge was refunded. Don't deliver it. |
| 409 | `pool_empty` | `POST /rewards` | Your pool doesn't hold the amount. Has `pool`. |
| 429 | `daily_limit` | `POST /rewards` | The player's daily limit from your pool. Has `dailyCapPerPlayer`. |
| 429 | `rate_limited` | `POST /notifications` | A notification limit. May have `retryAt`. |
| 400 | `bad_table_id`, `bad_seats`, `bad_taken`, `bad_title`, `bad_amount`, `bad_expires_in` | `POST /lobby/tables` | A field doesn't fit. |
| 429 | `too_many_tables` | `POST /lobby/tables` | The host has 3 open tables. Has `max`. |
| 409 | `full`, `own_table` | `POST /lobby/tables/:id/sit` | Every seat is taken, or the player is the host. |

`POST /session/verify` never errors for a bad token: it answers `{ "valid": false }`.
