Reference

Error codes

Errors arrive in two places: as standard HTTP responses for transport-level failures, and as a typed error field on the tool result for application-level failures. Both are documented below with resolution paths.

Two error layers

Transport-layer errors arrive as a non-200 HTTP response with a JSON body like {"error":"Unauthorized"}. They mean the request never reached a tool handler.

Tool-result errors arrive as a 200 HTTP response with a JSON-RPC tools/call result whose content[0].text deserializes to { ok: false, error: "<code>", ... } (or in the action-engine case, a McpActionResult with status: "error"). They mean the tool ran but couldn't complete.

Transport-layer errors

CodeHTTPWhat it meansHow to fix
unauthorizedHTTP 401Missing or invalid bearer token.Set the `Authorization: Bearer <token>` header on every request. The token is issued by the operator dashboard once available; in beta it is the shared secret stored in AWS Secrets Manager under `mcpBearerToken`.
quota_exceededHTTP 429Per-minute, daily call, or daily inference budget exceeded.Respect the `Retry-After` header (seconds). Per-minute window: 20 calls. Daily window: 1000 calls. Daily inference budget applies. See Rate limits.

Example: 401

http
HTTP/1.1 401 Unauthorized
Content-Type: application/json

{"error":"Unauthorized"}

Example: 429

http
HTTP/1.1 429 Too Many Requests
Retry-After: 47
Content-Type: application/json

{"error":"Too Many Requests"}

Retry-After is in seconds. For minute-window violations it bounds the wait until your oldest call falls out of the rolling 60s window; for daily-window violations it's the seconds until midnight UTC.

Tool-result errors

CodeWhat it meansHow to fix
invalid_argumentsTool input failed Zod validation.Check the field name, type, and required-ness in the tool reference. Empty strings on `min(1)` fields are the most common cause.
trust_insufficientThe current autonomy level for this (action class × counterparty) is too low for direct execution.Result will carry `status: "pending_approval"` and a `decision_id`. Wait for the user to approve via `action.approve` (Coming July 20, 2026) or surface the approval in your own UI.
action_engine_errorThe action engine refused to enqueue the decision (downstream failure, transient).Retry with exponential backoff. If it persists, inspect the ledger via the operator dashboard.
decision_expiredThe approval window for a pending decision has elapsed.Re-issue the original action call. Default approval window is 24 hours.
already_decidedThe decision was already approved or rejected.Inspect the ledger to see the resolution. The decision is idempotent once resolved.
invalid_decision_idNo pending decision matches the supplied id.Confirm the id came back from a recent `pending_approval` response and was not truncated.
feature_not_availableThe tool is registered but the underlying capability is not wired in this build.Currently affects `entity.resolve` and `entity.relationships` (consolidation pipeline pending). See the per-tool status pill.
not_implementedThe tool name is reserved but not yet exposed on this server.See the changelog for the planned ship date. Public surface is locked to the allow-list.
signature_invalidHMAC verification failed on a dispatch callback (`trigger.complete` / `trigger.reverse`).Confirm you are signing with the latest `webhook_secret` for the handler (rotate via `handler.rotate_secret` if you suspect a mismatch). The canonical signing string is documented in Dispatch. Replays are accepted within a 5-minute window; outside that window the signature is rejected.
handler_disabledThe registered handler is disabled (manual disable, repeated failed deliveries, or exceeded DLQ threshold).Re-enable via `handler.update` after fixing the underlying issue. Inspect recent delivery history with `trigger.history` and replay failed triggers with `trigger.replay` once the handler is healthy.
idempotency_conflictSame `Idempotency-Key` seen with a different request body.Idempotency keys must be unique per logical operation. If you intended a retry, ensure the body is byte-identical to the original. If you intended a new operation, generate a fresh key.
namespace_ownedaction_classes.register rejected because the namespace is owned by a different client_id within the same tenant.Choose a different namespace, or have the namespace-owning client_id register on your behalf. Namespace ownership is per-tenant and locked at first registration. Per ADR-0022 §1.
too_many_classesaction_classes.register over per-tenant quota.Deregister unused classes via `action_classes.deregister` when you are over quota. See Rate limits.
not_foundThe requested resource (action_id, decision_id, counterparty_id, etc.) was not found.Confirm the id came from a recent tool response and was not truncated. Tenant-scoped — cross-tenant ids never resolve.
write_failedThe tool could not persist the requested write (ledger, episode correction, etc.).Retry once. If it persists, check tenant context, encryption key availability, and operator logs.

Example: pending_approval (not an error, but a non-success outcome)

jsontools/call result
{
  "content": [
    {
      "type": "text",
      "text": "{\"status\":\"pending_approval\",\"decision_id\":\"dec_01HXP9\",\"message\":\"Trust insufficient for direct execution; routed to approval queue.\"}"
    }
  ]
}

pending_approval is the engine's designed response for actions that need user consent, not an exception. Treat it as a third lane alongside success and error.