m4Mindset docs

Docs / SDK / Build / Errors and resilience

View 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.

After this page your integration handles every failure an embedded agent can report: it tells the user something useful, retries only when a retry can work, and never repeats a payment or a booking by accident.

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 Where your agent can be reached
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 AMS
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-configurationAn attribute changed after configure()Call configure() again
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 AMS
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 AMS
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.

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