Operating system set to macOS
Documentation jurisdiction set to United States
Sign inConnect Claude

Error Handling

Know Exactly What Went Wrong

One error envelope, clear retry semantics, and a code for every failure. When Archie cannot finish a call, he tells you precisely why and what to do next. Branch on the code, never on the message.

The Error Envelope

Every error returns the same JSON shape. The type groups the failure; the code is the stable, machine-readable discriminator you branch on; the message is human-readable and may change, so never parse it.

error envelopejson
{
  "error": {
    "type": "validation_error",
    "code": "missing_field",
    "message": "workspace_id is required",
    "field": "workspace_id",
    "correlation_id": "req_01HX...",
    "retryable": false
  }
}

The correlation_id traces the full call in your dashboard. Quote it when you contact support, and log it so any failure is reproducible. retryable tells you whether trying again can help.

HTTP Status Codes

StatusMeaningWhat to do
400Bad request. Malformed body.Fix the request shape. Not retryable.
401Unauthorized. Missing or invalid credentials.Re-authenticate. See Authentication.
403Forbidden. Authenticated but out of scope.Request a broader scope, or use a key that has it.
404Not found, or you cannot see it.Check the id and your access. Not retryable.
409Conflict. A concurrent change or duplicate.Reconcile state, then retry once the conflict clears.
422Unprocessable. Understood but failed validation.Read field and fix the value.
429Rate limited.Honour Retry-After. See Rate limits.
5xxServer error on our side.Retry with exponential backoff.

Retry Semantics

Retry only what is safe to retry. Reads are idempotent and always safe. For writes through the skills API, send an Idempotency-Key header. A retried call carrying the same key within 60 seconds replays the original response instead of running again, so a network retry cannot post twice. A response built from a replay carries X-Idempotent-Replayed: true.

  • Retry on 429 and 5xx. Back off exponentially with jitter, capped at a handful of attempts.
  • Do not retry on 400, 401, 403, 404, or 422. The same request fails the same way. Fix it first.
  • On 429, read Retry-After (seconds) and wait at least that long. The rate-limit headers tell you when the window resets.
backoffpython
import time, random

def call_with_retry(fn, attempts=4):
    for i in range(attempts):
        resp = fn()
        if resp.status_code < 400:
            return resp
        if resp.status_code == 429:
            wait = float(resp.headers.get("Retry-After", 2 ** i))
            time.sleep(wait)
            continue
        if resp.status_code >= 500:
            time.sleep((2 ** i) + random.random())
            continue
        # 4xx other than 429: fix the request, don't retry
        resp.raise_for_status()
    resp.raise_for_status()

Code Dictionary

CodeTypeMeaning
auth_requiredauthenticationNo credential presented.
invalid_tokenauthenticationThe token is malformed, expired, or revoked.
insufficient_scopeauthorizationAuthenticated, but the scope does not cover this action.
workspace_not_foundauthorizationNo workspace context, or no access to it.
missing_fieldvalidationA required field is absent. See field.
invalid_fieldvalidationA field failed validation. See field.
not_foundnot_foundThe resource does not exist or is not visible to you.
conflictconflictA concurrent change or a duplicate write.
rate_limitedrate_limitToo many requests. Honour Retry-After.
key_credit_exhaustedrate_limitA test key spent its credit cap.
internal_errorserverAn unexpected failure. Retry with backoff; quote the correlation id.
One contract, every surface
REST, the SDKs, and the MCP tools all raise from this same envelope. Catch on code once and your handling works across surfaces.