kind— a stable, machine-readable category. Branch on this, never onmessage.message— a human-readable explanation. Wording may change.suggestion: present when the failure has a known fix: what to do next, often naming the exact operation to call (for example a stale edit lease points atwebsiteSourceCode.lease.acquire).validValues: present when the failing field accepts a closed set of values: the accepted values.- Some errors add further detail fields alongside these (for example
retryAfterSecondson rate limits, or framework diagnostics on failed builds).
Error kinds
validation
The request was well-formed HTTP but failed schema validation. The message names the first invalid field, and closed-set fields include their accepted values invalidValues.
Framework build failures also surface as
validation errors — on head/check, head/publish, and deployment/ensure — with diagnostic details describing each source error. Fix the source files they point at, then retry.unauthorized
No usable credential. The key is missing, malformed, expired, or revoked — or you sent a browser session cookie toapi.cactal.ai.
Authorization: Bearer <key> with an active key. If the key was rotated, update the stored secret. Repeated auth failures from one IP are rate limited — see Rate limits.
forbidden
The key authenticated, but its role does not grant the capability this endpoint requires (shown asx-cactal-required-capability on each endpoint page).
access to PATCH /v1/apiKeys/{apiKeyId}. Do not retry with the same key.
not_found
The resource does not exist — or it exists but is outside your key’s access scope. Cactal deliberately returns404 instead of 403 for cross-tenant requests so resource existence never leaks between organizations.
404 on an id it just received should stop and re-check its scope rather than retry.
conflict
The request contradicts current state: a duplicate slug or field key, a resource cap reached, or a delete blocked by incoming references. The message states the specific conflict.rate_limited
You exceeded a rate-limit bucket. The response includes aRetry-After header (seconds) and mirrors it in the body:
retryAfterSeconds, then retry. Sustained 429s mean you should queue and pace requests — see Rate limits for the exact buckets.
internal
Something failed on Cactal’s side. The request may or may not have taken effect.x-cactal-idempotent: false), read the resource first to confirm whether the change landed before retrying.
Errors over MCP
MCP tool calls run the identical pipeline. Failures come back as tool results withisError: true and the same { kind, message, ... } payload as JSON content, so agents can branch on kind exactly like HTTP clients do.