Guides

Errors

A tool error is a result with isError: true and the problem in the first text block. Branch on its code.

The server reports a failed call as a result, not as a JSON-RPC error. The result has isError: true and the RFC 9457 problem of the REST API as JSON in content[0].text. It has no structuredContent: the specification requires structuredContent to match the tool's outputSchema, and an error does not.

A 429 result
{
  "isError": true,
  "content": [
    {
      "type": "text",
      "text": "{\"type\":\"https://docs.filmo.io/errors/rate_limited\",\"title\":\"Rate limit exceeded\",\"status\":429,\"code\":\"rate_limited\",\"detail\":\"Rate limit exceeded: retry in 17 seconds\",\"requestId\":\"req_…\",\"retryAfter\":17,\"meta\":{\"rateLimit\":{\"limit\":120,\"remaining\":0,\"reset\":17}}}"
    }
  ]
}

The problem carries type, title, status, code, detail, requestId and, when they apply, errors[], requiredScope, requiredAddon and retryAfter. A call with an idempotency key also returns meta.idempotencyKey inside the problem. Quote requestId when you ask for help. It is the same problem document the REST API returns: see Errors.

A malformed request, an unknown method or an unknown tool is a JSON-RPC error with a code such as -32600, -32601, -32602 or -32700, and its message is in English. A call without a credential is not JSON-RPC: it is an HTTP 401 with a problem body. See the protocol reference.

Client.callTool of the TypeScript SDK and call_tool of the Python SDK return the result; neither raises for a tool error. Check isError and parse the first text block. Only protocol failures, such as an unknown tool or a timeout, raise.

const result = await client.callTool({ name, arguments: args });
if (result.isError) {
  const text = (result.content as { type: string; text?: string }[]).find((c) => c.type === "text")?.text ?? "{}";
  const problem = JSON.parse(text); // { code, status, detail, requestId, retryAfter?, requiredScope? }
  if (problem.code === "rate_limited") await sleep(problem.retryAfter * 1000);
}

In the 2.x packages the field is is_error in Python and the transport is built differently; the contract of the server is the same. See Build your own client.

CodeStatusWhat to do
unauthorized401Send a credential. The api_key_* codes say why a key is not valid: revoked, expired or its owner is no longer an administrator.
insufficient_scope403The key or connection lacks requiredScope. See below.
validation_error400Fix the fields listed in errors[] and call again.
not_found404The record does not exist in this account.
conflict409Read errors[0].code for the cause; read the record again before retrying.
precondition_failed412The record changed since you read it. Read it again.
idempotency_key_reused422That key was used with different arguments. Use a new key.
rate_limited429Wait retryAfter seconds.
addon_required402The account needs the add-on named in requiredAddon.
service_unavailable503The feature is switched off for the account; retrying does not help.

A tool that needs a scope the connection does not have returns insufficient_scope with requiredScope. The server does not answer with a 403 that asks the client to widen its scopes, so no client adds the scope by itself. To add one, remove the connector and add it again, and check the scope in the consent screen. With a key, create another key with the scope.

The decision table of the REST API applies. A result tells you it is a repetition with meta.idempotentReplay: true.

StatusIdempotency-KeyIdempotent-Replay: trueWhat to do
409 conflict · in_progressYesNoerrors[0].code is in_progress: another request with the same Idempotency-Key is still running. Wait and retry with the same key (exponential backoff, up to 3 times): you get its stored response when it finishes. If the 409 persists for about two minutes, the reservation is treated as abandoned and the next attempt with the same key runs; read the resource before retrying.
409 conflict · lock_timeout—Noerrors[0].code is lock_timeout: another write held the resource, or the database aborted the transaction on a transient conflict; nothing was written. Retry with the same key and an exponential backoff, up to 3 times; if it persists, read the resource again.
409 conflict · invalid_state——errors[0].code is invalid_state: the state of the resource forbids the operation (a unique violation, a line of an atomic batch, an item deleted while saving). Do not retry unchanged: waiting never fixes it. Read the resource again; if the request still fits, you may send it again.
409 contact_in_use · address_in_use · insufficient_stock——Do not retry. errors[] says what blocks the operation: fix that first.
412 precondition_failed——Read the resource again, redo your change on top of it and send the new ETag.
422 idempotency_key_reusedYes—A bug in the client: the key was used with a different request (a different method, path or body, compared byte by byte). Use a new key only if it really is a different operation.
429 rate_limited——Wait the seconds in Retry-After and retry with the same key.
500 internal_errorYesYesStop. The failure happened after the commit and is stored and repeated for 24 h with that key: read the resource to see where it ended up.
500 internal_errorYesNoRetry with exponential backoff and the same key.
500 internal_errorNo—Read before retrying a write: without a key nothing protects you from a duplicate.
503 service_unavailable——Do not retry: the feature is switched off for the account.
207 (returnOrderLineItems with atomic: false)Yes—The lines that failed were not returned and the rest were. Resend only the failed ones with a new key; repeating the key would return the same 207.
413 · 502 · 504 (not application/problem+json)Yes—An answer from the platform, not from the API: decide by the status and retry with the same key. A 413 is not fixed by retrying (the body is too large).
Any validation 4xx——Fix the request and resend it; never in a loop.
401 · 402 · 403 (key, owner or plan)——Do not retry: resending the same request fails again. Act on the key, its owner or the plan: use a valid key with the right scope, ask an administrator for the missing permission, or add the add-on.

Every error code has its own page in the REST API tab, with when it happens and what to do.

  • Guides

    Safe writes

    An assistant that can write can also write the wrong thing. Decide which tools need a person, retry without duplicating, and know what a write sets in motion.

  • Reference

    Protocol reference

    What a client needs to talk to the server without a ready-made one.