Errors
Every error the in-app SDKs raise — the error type on each platform, every code with its HTTP status, meaning, the methods that raise it and the fix — plus the other ways a call, the bar or the microphone reports trouble.
A method that asks Graine on the customer's behalf — memory, scheduled calls, device registration — fails with one error type carrying a code. Branch on the code; show the message, which is written for the customer.
The error type
Every code
| Code | HTTP | Means | Raised by | Fix |
|---|---|---|---|---|
invalid_key | 401 | The publishable key is not one, or belongs to no agent | Every method that asks Graine | Copy the key again from Agent → Embed & Widgets → Install |
origin_refused | 403 | The app or site is not allowed: app:// (apps) or the site's host is missing from Allowed domains | Every method that asks Graine | Add app://, or the exact host |
rate_limited | 429 | Too many requests from this install, key or address | Every method that asks Graine | Wait retryAfterSeconds, then try again |
invalid_request | 400, 422 | A field is missing or malformed — a time without its offset, a reason over 120 characters | scheduleCall; device registration | Fix the request. Flutter also raises it on the device for a reason over 120 characters |
identity_required | 400 | No customer is identified, or the identifier is a placeholder | memories, forgetMemory, scheduleCall, scheduledCalls, cancelScheduledCall when the call is a customer's | Call identify() with the identifier first — Identity |
device_limit | 403 | This install is not one of the customer's allowed devices | memories, forgetMemory, newConversation, scheduleCall | Sign out on another device, raise Devices per user, or reset the customer's devices |
scheduling_off | 403 | Users, from your app is off. Checked on the device first, from schedulingRules | scheduleCall | Turn it on under Calls & scheduling, or hide booking when usersCanBook is false |
too_soon | 400 | Earlier than schedulingRules.earliest(now). Checked on the device first (React Native and the website allow 30 seconds' grace) | scheduleCall | Offer times from earliest(now) |
too_far | 400 | Later than schedulingRules.latest(now). Checked on the device first | scheduleCall | Offer times up to latest(now) |
outside_window | 400 | Outside the agent's calling window, in the customer's time zone | scheduleCall | Show the message — it names a time that works — and offer another time |
too_many_pending | 409 | The customer already has maxPendingPerUser calls waiting | scheduleCall | Cancel one first |
not_found | 404 | No such booking for this customer or install | cancelScheduledCall | Refresh scheduledCalls() |
unavailable | 503, 504 | Graine could not answer just now — or, for scheduleCall, the workspace is out of credit or the agent is switched off | Every method that asks Graine | Try again with backoff |
network | — | The request never got an answer: offline, or the connection dropped | Every method that asks Graine | Try again when the device is online |
unknown | other | A code newer than this SDK, or an unexpected answer — carrying its word | Every method that asks Graine | Log code and message; show the message |
The scheduling codes scheduling_off, too_soon, too_far, outside_window and
too_many_pending come with a message written for the customer — show it as it
is. scheduling_off, too_soon and too_far are usually refused on the device,
before any request: those carry no HTTP status (status 0). A newer code reaches
an older SDK as unknown, carrying its word — .unknown(raw) in Swift,
Unknown(raw) on Android and in Kotlin Multiplatform, the word itself in
TypeScript and on the website, and codeName beside unknown in Dart.
When the bar cannot start
The bar's voice and chat report setup problems through onError (and, in React
Native, the launcher's error), not as a coded exception:
| Code in the message | HTTP | Means | Fix |
|---|---|---|---|
key_missing | 401 | No key reached Graine | Update the SDK |
key_malformed | 401 | Not a publishable key — they start with pk_live_ | Use the agent's publishable key |
key_unknown | 401 | No agent has that key | Copy it again |
no_origin | 403 | An app was refused: app:// is not in Allowed domains | Add app:// |
no_domains_configured | 403 | The allowlist is empty, which denies everything | Add app:// and your sites |
origin_not_allowed | 403 | A site is not in Allowed domains | Add the exact host |
not_configured | 503 | High-quality audio is not enabled for your workspace; calls use the standard path | Ask Graine to enable it |
Other ways trouble is reported
| What | How it arrives | Values |
|---|---|---|
| The bar's audio moved to the standard path | onTransport(transport, requested, reason, message) | not_configured (a setting — never fixes itself), unreachable, screen_channel, media (answered with no audio) |
| The microphone was refused | onMicDenied(reason), micDenied | NotAllowedError (refused), NotFoundError (no usable microphone) |
| A native call never connected | Outcome failed, with its reason | A sentence for your logs — Calling reference → Outcomes |
| A native call's line broke | Outcome ended, reason failed | — calls do not reconnect |
| Calls configured wrongly (React Native, Flutter) | configure rejects with code invalid_options | The message names the option, such as callScreen must be "sdk" or "app" |
| An action took too long | The agent is told it did not finish | 6 seconds |
| A component's endpoint failed | The authored content is shown | 8 seconds |
Errors carry nothing personal: the message never contains the identifier, the customer's traits or the push token.
The structures behind these: Types → GraineError.

