# Handle errors

## Error model[​](#error-model "Direct link to Error model")

Rejected promises and the `error` event carry a [`NablaConnectError`](/connect/sdk/reference/classes/NablaConnectError.md) with a stable `code` and an optional `reason`. Branch on `code`.

* `create` throws a `TypeError` when the iframe does not belong to a window. Nothing else throws.
* The `error` event is a [`NablaConnectErrorEvent`](/connect/sdk/reference/classes/NablaConnectErrorEvent.md), a `CustomEvent` with the error on `detail`. It fires for an error after `ready` that no command owns, like `CONTEXT_CHANGED`.
* Failures before `ready` reject `ready` only. A rejected `ready` ends the client: create another one to retry. `TIMEOUT` is the exception, see below.
* After a `ready` `TIMEOUT`, a failure that ends the client arrives on the `error` event, since `ready` already settled.
* After `ready`, an unexpected failure inside the SDK is an `UNKNOWN` error event, and the client keeps running.

## Error codes[​](#error-codes "Direct link to Error codes")

### `COMMAND_REJECTED`[​](#command-rejected "Direct link to command-rejected")

The iframe refused or failed the command. `reason` is `NOT_ALLOWED`, `STALE_STATE`, `INVALID_SESSION`, `EXPORT_FAILED`, or `UNKNOWN` (an unexpected failure while running it). Re-read `getCurrentState()` and retry if the command is still allowed.

### `BUSY`[​](#busy "Direct link to busy")

The iframe cannot run the command right now: another command is running, the encounter is changing state, or the provider is busy in Nabla, for example during [their first session](#first-session). Wait for the next `stateChanged`.

### `TIMEOUT`[​](#timeout "Direct link to timeout")

No answer in 30 seconds.

* On `ready`: usually a setup problem, see [Troubleshooting](#troubleshooting). The client keeps listening, and a late first state arrives on `stateChanged`. Commands called before it reject with `TIMEOUT`.
* On a command: `reason` is `NO_RESULT`. The outcome is unknown, not failed. See [Timeouts](/connect/guides/sdk/commands.md#timeouts).

### `CONTEXT_CHANGED`[​](#context-changed "Direct link to context-changed")

Another client was created on the same iframe, or another page loaded in it. Call `destroy()` and create a new client. A reload of the iframe does not cause it: the client reconnects.

### `UNSUPPORTED_VERSION`[​](#unsupported-version "Direct link to unsupported-version")

This SDK version cannot talk to the current iframe. Upgrade `@nabla/connect-sdk`.

### `INVALID_ORIGIN`[​](#invalid-origin "Direct link to invalid-origin")

The `url` you passed to `create` is not a Nabla Connect encounter URL. Pass the URL from your backend unchanged.

### `INVALID_MESSAGE`[​](#invalid-message "Direct link to invalid-message")

The iframe could not read a command. The error arrives on the command's promise, or on the `error` event when no command can be matched. Report it to <connect@nabla.com>.

### `DESTROYED`[​](#destroyed "Direct link to destroyed")

`destroy()` was called. Nothing to do.

### `UNKNOWN`[​](#unknown "Direct link to unknown")

The iframe sent something this SDK version does not understand (upgrade `@nabla/connect-sdk`), or an unexpected failure happened. After `ready`, the client keeps running: read the state before you retry a command.

## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting")

### `ready` rejects with `TIMEOUT`[​](#ready-rejects-with-timeout "Direct link to ready-rejects-with-timeout")

The iframe sent no state within 30 seconds. Check, in this order:

1. Nabla enabled the SDK for your organization, and your origin shows in the allowlist of Nabla Connect Admin. Nabla Connect Admin shows whether the front integration and the SDK are on.
2. `POST /encounters` was called with `target_origin`, and it equals the origin of the page that embeds the iframe. When it is missing, the iframe console logs a warning.
3. That origin is in the allowlist, with the same scheme, hostname, and port.
4. The `url` you passed to `create` is an encounter URL, not a settings URL. Check `iframe.src` and the network tab.
5. No intermediate frame sits between your page and the iframe.

The client keeps listening after this rejection. If the state comes late, it arrives on `stateChanged`.

### A new provider's first session[​](#first-session "Direct link to A new provider's first session")

The first time a provider opens Nabla, the iframe signs them in and shows the Nabla onboarding over the encounter. The provider completes it in the iframe.

1. The first state arrives within seconds, and `ready` resolves. It is the real state, usually `NOT_STARTED`, with an empty `availableActions`.
2. Until the onboarding is over, commands reject with [`BUSY`](#busy).
3. When it is over, a `stateChanged` brings the available actions.

To handle it:

* Keep the iframe visible. The provider needs it to finish the onboarding.
* Enable your controls from `availableActions` only. A command sent only when its action is listed does not get this `BUSY`.
* Do not start recording as soon as `ready` resolves. To start it automatically, wait for the first `stateChanged` that lists `START_RECORDING`.
* Keep the client. It receives the actions on its own, so do not destroy it and create a new one.

### A command rejects with `TIMEOUT`[​](#a-command-rejects-with-timeout "Direct link to a-command-rejects-with-timeout")

The iframe did not answer within 30 seconds. This does not mean the command failed: see [Timeouts](/connect/guides/sdk/commands.md#timeouts).

* Read `stateChanged` or `getCurrentState()` to learn the outcome. The new state arrives when the command ends.
* A new command before then rejects with `BUSY`.
