ReferenceSDKErrors and resilience

Updated 27 Oct 2026View as Markdown

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.

Where failures show up#

Where it failsDrop-in elementUI-less client
Your server's session create callAn HTTP error your backend receivesThe same
getSession returns something the SDK can't usemindset:error with invalid-authA session_error event
getSession throwsNothing at once. The next turn fails with turn-failedNothing at once. The next turn fails, and its promise rejects
Your code calls the element wronglymindset:error with a configuration codeNot applicable
A turn failsmindset:error with turn-failed, plus a run_error runtime event when the run itself failedA run_error event when the run itself failed, and the promise from send() rejects
A turn stops at a limitA complete runtime event carrying boundThe 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 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.

SignalWhat happenedWhat to do
Create: 400 validation_errorYour request body is wrong, or the agent has no published version to embedFix the request or publish the agent. Retrying the same call won't help
Create: 403 agent_not_available_hereEmbedded in your own product isn't ticked for the agentAn org admin ticks it. See Build an agent
Create: 404 not_foundWrong org slug, environment slug or agent; a key without admin scope; a personal agent; or an unknown user without createUserIfNeededCheck each against the agent's Embed tab. Retrying won't help
Create: 409 user_not_activeThe user is invited but not yet active, or was removedDon't retry. Tell the user they don't have access
Create: 409 no_version_in_forceNo configuration version is activeActivate one in the Studio
Create: a 5xx, or no answerMindset or the network failedReturn an error from your endpoint. The SDK calls getSession again the next time it needs a session
getSession throwsYour endpoint failed or was unreachableNothing 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-credentialgetSession returned something the SDK can't readReturn your endpoint's parsed JSON body (r.json()) unchanged. Not the Response, and not a field from inside it
session_error with expired-session-credentialThe session was readable but already used upCreate a new session on every getSession call. Don't cache one
missing-agentconfigure() ran with no agentSet the agent attribute
not-configuredsend() or a set method ran before configure(), or after the element left the pageCall configure() first
stale-configurationThe agent or conversation-id attribute changed after configure()Call configure() again
invalid-attributeThe situational-awareness or passthrough-params attribute isn't a JSON object, or is over its size limitFix the attribute. The agent keeps what it had
invalid-configurationThe element can't do what it was configured to doFollow the message, which names the setting
turn-failedA turn failed: network, credential or the runIf 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, cancelledSomeone stopped the turn (aborted: true)Show "stopped". It isn't an error
run_error, model_errorA model call failedOffer a retry, after checking effects
run_error, step_limitThe run used its whole step budgetOffer to continue or retry, after checking effects. If it keeps happening, the agent needs work in the Studio
run_error, tool_schemaThe model provider refused one of the agent's tool definitionsDon't retry. It fails the same way until the tool is fixed in the Studio
run_error, unavailableThe agent can't run as asked, for example it has no version in forceDon't retry. Tell the user the assistant isn't available
run_error, stale_configurationThe agent's active version changed, and the SDK's own retry didn't get past itRetry once more. The SDK has already fetched the new configuration
run_error, failedAnything elseOffer a retry, after checking effects
complete with boundThe turn stopped at a limit: step_limit, deadline, spend_limit or repetition. It is complete, and can be continuedShow 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.

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 covers what the ceiling covers and what it doesn't.