# Errors and resilience

> Every way an embedded agent can fail, from the session call to a turn, and what your code should do about each one.

In the browser, the SDK runs the conversation with Mindset, with your page tools and widgets. It gets its session from your server, which creates it with your org API key. Mindset holds the agent and runs its model and tools. Highlighted: Browser.

## Where failures show up

| Where it fails                                   | Drop-in element                                                                                 | UI-less client                                                                        |
| ------------------------------------------------ | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Your server's session create call                | An HTTP error your backend receives                                                             | The same                                                                              |
| `getSession` returns something the SDK can't use | `mindset:error` with `invalid-auth`                                                             | A `session_error` event                                                               |
| `getSession` throws                              | Nothing at once. The next turn fails with `turn-failed`                                         | Nothing at once. The next turn fails, and its promise rejects                         |
| Your code calls the element wrongly              | `mindset:error` with a configuration code                                                       | Not applicable                                                                        |
| A turn fails                                     | `mindset:error` with `turn-failed`, plus a `run_error` runtime event when the run itself failed | A `run_error` event when the run itself failed, and the promise from `send()` rejects |
| A turn stops at a limit                          | A `complete` runtime event carrying `bound`                                                     | The same, on `on()`                                                                   |

On the element, runtime events arrive as `mindset:runtime-event`. On the UI-less client, everything arrives on `chat.on()`. The [events reference](https://docs.mindset.ai/docs/events-reference) has every field.

## What to do about each one

Branch on the code or reason, never on the message text. Treat a code you don't recognize as a general failure, because new ones can be added.

| Signal                                                               | What happened                                                                                                                           | What to do                                                                                                                                                                                    |
| -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Create: 400 `validation_error`                                       | Your request body is wrong, or the agent has no published version to embed                                                              | Fix the request or publish the agent. Retrying the same call won't help                                                                                                                       |
| Create: 403 `agent_not_available_here`                               | **Embedded in your own product** isn't ticked for the agent                                                                             | An org admin ticks it. See [Build an agent](https://docs.mindset.ai/docs/build-an-agent)                                                                                                                             |
| Create: 404 `not_found`                                              | Wrong org slug, environment slug or agent; a key without admin scope; a personal agent; or an unknown user without `createUserIfNeeded` | Check each against the agent's **Embed** tab. Retrying won't help                                                                                                                             |
| Create: 409 `user_not_active`                                        | The user is invited but not yet active, or was removed                                                                                  | Don't retry. Tell the user they don't have access                                                                                                                                             |
| Create: 409 `no_version_in_force`                                    | No configuration version is active                                                                                                      | Activate one in the Studio                                                                                                                                                                    |
| Create: a 5xx, or no answer                                          | Mindset or the network failed                                                                                                           | Return an error from your endpoint. The SDK calls `getSession` again the next time it needs a session                                                                                         |
| `getSession` throws                                                  | Your endpoint failed or was unreachable                                                                                                 | Nothing extra. The SDK asks again later. Make sure your endpoint returns a non-2xx status and your function throws, rather than returning an error body                                       |
| `invalid-auth`, or `session_error` with `invalid-session-credential` | `getSession` returned something the SDK can't read                                                                                      | Return your endpoint's parsed JSON body (`r.json()`) unchanged. Not the `Response`, and not a field from inside it                                                                            |
| `session_error` with `expired-session-credential`                    | The session was readable but already used up                                                                                            | Create a new session on every `getSession` call. Don't cache one                                                                                                                              |
| `missing-agent`                                                      | `configure()` ran with no agent                                                                                                         | Set the `agent` attribute                                                                                                                                                                     |
| `not-configured`                                                     | `send()` or a `set` method ran before `configure()`, or after the element left the page                                                 | Call `configure()` first                                                                                                                                                                      |
| `stale-configuration`                                                | The `agent` or `conversation-id` attribute changed after `configure()`                                                                  | Call `configure()` again                                                                                                                                                                      |
| `invalid-attribute`                                                  | The `situational-awareness` or `passthrough-params` attribute isn't a JSON object, or is over its size limit                            | Fix the attribute. The agent keeps what it had                                                                                                                                                |
| `invalid-configuration`                                              | The element can't do what it was configured to do                                                                                       | Follow the message, which names the setting                                                                                                                                                   |
| `turn-failed`                                                        | A turn failed: network, credential or the run                                                                                           | If a `run_error` runtime event came with it, follow its row below. A network or credential failure (an expired session, a `getSession` that threw) has no `run_error`. Let the user try again |
| `run_error`, `cancelled`                                             | Someone stopped the turn (`aborted: true`)                                                                                              | Show "stopped". It isn't an error                                                                                                                                                             |
| `run_error`, `model_error`                                           | A model call failed                                                                                                                     | Offer a retry, after checking `effects`                                                                                                                                                       |
| `run_error`, `step_limit`                                            | The run used its whole step budget                                                                                                      | Offer to continue or retry, after checking `effects`. If it keeps happening, the agent needs work in the Studio                                                                               |
| `run_error`, `tool_schema`                                           | The model provider refused one of the agent's tool definitions                                                                          | Don't retry. It fails the same way until the tool is fixed in the Studio                                                                                                                      |
| `run_error`, `unavailable`                                           | The agent can't run as asked, for example it has no version in force                                                                    | Don't retry. Tell the user the assistant isn't available                                                                                                                                      |
| `run_error`, `stale_configuration`                                   | The agent's active version changed, and the SDK's own retry didn't get past it                                                          | Retry once more. The SDK has already fetched the new configuration                                                                                                                            |
| `run_error`, `failed`                                                | Anything else                                                                                                                           | Offer a retry, after checking `effects`                                                                                                                                                       |
| `complete` with `bound`                                              | The turn stopped at a limit: `step_limit`, `deadline`, `spend_limit` or `repetition`. It is complete, and can be continued              | Show the reply, and let the user continue. `bound.limit` holds the limit's value                                                                                                              |

## Check effects before you retry

A turn can fail after the agent has already done something. `run_error.effects` lists the tool calls that changed something and finished before the failure. Each has `toolName`, `toolCallId` and `access`, and may have a `reference` (a receipt the system returned) and `output`.

> **Warning:** If `effects` has entries, a plain retry may repeat them: a second refund, a second booking. Tell the user what already happened, and let them decide.

This UI-less handler puts the rows above into code:

```javascript
chat.on((event) => {
  if (event.type !== "run_error") return;

  if (event.aborted) {
    showNotice("Stopped.");
    return;
  }

  const done = event.effects ?? [];
  if (done.length > 0) {
    const list = done.map((e) => e.reference ?? e.toolName).join(", ");
    showNotice(`Something went wrong, but these already happened: ${list}. Check before trying again.`);
    return;
  }

  switch (event.reason) {
    case "tool_schema":
    case "unavailable":
      showNotice("The assistant isn't available right now.");
      break;
    case "model_error":
    case "step_limit":
    case "stale_configuration":
    case "failed":
    default:
      showRetryButton(() => chat.retry().catch(() => {}));
      break;
  }
});
```

`showNotice` and `showRetryButton` stand for your own interface code. `retry()` runs the last turn again and replaces its reply. Its promise rejects if that turn fails too, and the `run_error` event reports it, so the empty `catch` only stops an unhandled rejection.

On the element, listen for `mindset:runtime-event` and read `e.detail` the same way:

```javascript
const agent = document.querySelector("mindset-agent");

agent.addEventListener("mindset:runtime-event", (e) => {
  const event = e.detail;
  if (event.type === "run_error" && !event.aborted && event.effects?.length) {
    console.warn("A turn failed after changing something:", event.effects);
  }
});

agent.addEventListener("mindset:error", (e) => {
  const { code, message } = e.detail;
  if (code === "invalid-auth") console.error("Fix getSession:", message);
  else if (code === "stale-configuration") reconfigure(); // your own function that calls configure() again
  else console.error("Mindset error:", code, message);
});
```

## What the SDK already handles

- **Renewal.** The SDK renews its access token while the page is open, and calls `getSession` again when it can't.
- **A changed agent version.** When the agent's active version changes mid-conversation, the SDK fetches the new configuration and runs the turn again, once, before you see anything. That works while the turn hasn't produced output yet.
- **A conversation ID it can't use.** It starts a fresh conversation and tells you with `conversation_id` (`mindset:conversation` on the element), with `replacedBecause: "discarded"`. Store whatever ID arrived last.
- **History that won't load.** `history_settled` still arrives, with an empty list, within 15 seconds. It can't tell you whether the history was empty or failed to load.

## Spend limits

Each person using an embedded agent has an hourly spend ceiling on their turns, roughly $25 an hour per person on each Mindset server. Past it, their turns are refused until spend in the window drops, and a refused turn fails like any other. [See and control cost](https://docs.mindset.ai/docs/see-and-control-what-it-costs) covers what the ceiling covers and what it doesn't.

## Related

- [Events reference](https://docs.mindset.ai/docs/events-reference) for every event and field.
- [The mindset-agent element](https://docs.mindset.ai/docs/the-mindset-agent-element#error-codes) for the element's error codes.
- [Session API](https://docs.mindset.ai/docs/session-api#errors) for every create error.
