Errors
Failures arrive through two different channels, and the distinction matters: one is a broken request, the other is something the model should read and react to.
Tool errorsβ
Everything the caller can act on β an invalid argument, not found, insufficient
scope or permission, a route error β comes back as a successful JSON-RPC
response whose result is marked isError, carrying the API's own error
envelope as text:
{ "jsonrpc": "2.0", "id": 2, "result": {
"isError": true,
"content": [{ "type": "text",
"text": "{ \"error\": { \"code\": \"NOT_FOUND\", \"message\": \"Execution not found\" } }" }]
} }
The code vocabulary is the v1 API's, unchanged. The
ones you will meet most:
code | When |
|---|---|
VALIDATION_ERROR | The arguments failed validation β the tool's own (Invalid tool arguments.) or the route's; the details name the fields. |
NOT_FOUND | The record does not exist, was removed or belongs to another organization. |
CONFLICT | State conflict β for example, an environment name already in use. |
IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_REQUEST_IN_PROGRESS | See Idempotency. |
FEATURE_NOT_ENABLED | Kobana has not enabled this operation for the organization. |
ENTITLEMENT_REQUIRED | A run without Kadu in the organization's plan. |
MISSING_SECRETS, ENVIRONMENT_REQUIRED | A run with required secrets missing a value in the environment, or without a valid environment. kadu_tasks_readiness_get checks beforehand. |
COST_LIMIT_REACHED | The organization reached its monthly cost limit. |
RATE_LIMITED | The route's limit was reached β see Rate Limiting. |
USER_DECLINED | In era 2026-07-28, you declined or cancelled the confirmation of a destructive tool. Nothing changed. |
confirmation_required | The tool is destructive, the client cannot confirm in the chat and the token has no person of the organization who could open an approval link. Nothing ran. |
confirmation_replayed | The confirmation sent had already been used β each one runs a call once. Call the tool again to be asked again. Nothing ran. |
confirmation_unavailable | The server could not record the confirmation (or the approval by link) right now, so it refused the tool. Nothing ran; try again later. |
And the three the MCP itself adds, because they are about authorization, not about the record:
code | Who fixes it |
|---|---|
insufficient_scope | The token's scope does not reach the tool: the resource is not in the token, or the level it requires (write or delete) is missing. Authorize again granting the missing access, or use a personal token that reaches the resource at the required level. |
insufficient_permission | An administrator of the organization, granting the permission the error names. |
not_a_member | Authorizing the client again with an account that is a member of the organization β no permission fixes this. |
These three are separate on purpose: they are different conversations, with different people. A single error would say "you can't" without saying whom to ask. See Authentication.
Not everything that does not run is an errorβ
input_required. In era2026-07-28, the firsttools/callof a destructive tool may returnresultType: "input_required"with a confirmation request for the user. The client shows the question and re-sends the same call with the answer.approval_required. In other clients, the same call returns, withoutisError,status: "approval_required"with the approval link. Show the link; do not call the tool again.
Both are the normal flow β see Confirming destructive tools.
Protocol errorsβ
A malformed message gets a JSON-RPC error object, not a result:
| Code | Meaning |
|---|---|
-32700 | Parse error β the body is not valid JSON (HTTP 400) |
-32600 | Invalid request β not a JSON-RPC object, an array (batching is not supported) or a body above 1 MB |
-32601 | Method not found (HTTP 404 in era 2026-07-28) |
-32602 | Invalid params β unknown tool or prompt, missing name, invalid prompt argument, resource without permission; in era 2026-07-28, also resource not found |
-32002 | Resource not found β 2025 era only (2026-07-28 uses -32602) |
-32603 | Internal error β unexpected failure running the tool |
-32020 | Header mismatch β in era 2026-07-28, an MCP header does not match the body (HTTP 400) |
-32022 | Unsupported protocol version (HTTP 400) |
{ "jsonrpc": "2.0", "id": 3,
"error": { "code": -32601, "message": "Method not found: tools/describe" } }
Transport errorsβ
Refusals that happen before the JSON-RPC message is read are plain HTTP
responses, with the { "error": { "code", "message" } } envelope: missing or
invalid token, unknown organization, Origin outside the list, rate limit,
body too large, token verification unavailable. They are listed in
Authentication β Failure modes.
Why isError and not a JSON-RPC errorβ
A 404 on an execution is not a defect of the MCP client β it is information
about the world, and the model on the other side needs it to correct itself.
Burying it in a protocol error would make the client treat it as a transport
failure, hide the text and, at best, retry the identical call. Hence the rule:
if the caller can act on it, it is a result; if the request is broken, it is
an error.
Observabilityβ
Each tools/call writes one structured log line with the tool, the
organization, the token id, the token kind (OAuth flow or personal), the status
and the duration β never the arguments, the token or the body. What the tool
does goes through the same API route, with the same effects: a run becomes an
execution like any other, with the same events and webhooks. Approvals by link
are recorded in Settings βΊ Approvals, with what was asked, the decision,
when and the result.