guides
Intended Documentation
Error Patterns and Remediation
The real error codes the Intended runtime returns — auth, validation, decision, token, and rate-limit failures — with the exact HTTP statuses, the fail-closed semantics, and how to handle each.
Error Patterns and Remediation#
Intended is fail-closed: when a request cannot be authenticated, validated, or evaluated, it is denied rather than allowed through. This page lists the error codes the runtime actually returns and how to handle each.
Warning
Error body shape is not uniform across routes. Some return a flat { "error": "<CODE>", "message": "…" }; others nest as { "error": { "code": "…", "message": "…" } }. Read the HTTP status first — it is the reliable signal — and treat the body as supplementary. Do not assume a request_id or details field on every error.
Authentication and authorization#
These come from the auth gate before any route handler runs. The full credential model is in API Authentication.
| Status | Code | Cause | What to do |
|---|---|---|---|
401 | UNAUTHORIZED | No valid credential. | Check the Authorization: Bearer intended_live_… header and the key value. |
401 | INVALID_PORTAL_SESSION | Idle or expired portal session. | Re-authenticate the session. |
403 | TENANT_MISMATCH | Credential's tenant ≠ requested tenantId. | Send the correct x-tenant-id / body tenantId. |
403 | FORBIDDEN | Authenticated but missing the required permission. | Grant the named scope/role to the key (e.g. intent:create). |
403 | IP_NOT_ALLOWED | Source IP not on the tenant allowlist. | Call from an allowlisted address. |
403 | MFA_REQUIRED | Privileged portal role without MFA. | Complete MFA on the session. |
Validation#
| Status | Code | Cause | What to do |
|---|---|---|---|
400 | VALIDATION_ERROR | The body failed schema validation. | Fix the request. For POST /intent, the body is an IntentRequest: tenantId, actor{id,type}, targetSystem, proposedAction, riskContext{…, github{…}}. targetSystem must equal "<github.owner>/<github.repo>". |
Note
There is no action / resource / context triple. The /intent request is an IntentRequest with a required riskContext.github target. Sending the wrong shape produces 400 VALIDATION_ERROR.
Decision outcomes#
A decision is not an error — DENIED is the system working correctly. But it surfaces as HTTP 403, so handle it deliberately.
| Status | authorityDecision.decision | Meaning | What to do |
|---|---|---|---|
200 | APPROVED | Authorized; token minted. | Verify the token, then execute. |
202 | ESCALATED | Needs human approval; no token. | Route to your approval queue. |
403 | DENIED | Not authorized (default-deny on no match). | Surface the rationale; do not retry as-is. |
500 | AUTHORITY_LOOP_FAILED | Evaluation failed internally. | Fail closed — the action was not authorized. Retry with backoff; if persistent, contact support. |
Info
No matching policy is a deny, not an error. The engine is default-deny: an action that matches no policy is denied. If you expected an approval, author a policy that covers the intent pattern.
Token verification#
These are returned by @intended/verify's verifyToken (as the reason field), or surfaced by a connector's BaseAdapter on a rejected execution. The complete list and the verification flow are in Verify Decision Tokens.
| Reason | Cause | Action |
|---|---|---|
KID_MISMATCH | Token kid ≠ pinned key id. | Refresh the tenant keys; re-pin the kid. |
SIGNATURE_VERIFICATION_FAILED | Bad signature / wrong key / issuer / audience. | Reject; possible tampering or wrong key. |
TOKEN_TENANT_MISMATCH | Token tenant ≠ expected. | Reject; token replayed across a boundary. |
TOKEN_ADAPTER_MISMATCH | Token adapter ≠ this execution. | Reject; wrong token for this adapter. |
TOKEN_EXPIRED | Past expiresAt. | Re-submit POST /intent for a fresh token. |
TOKEN_TTL_EXCEEDS_LIMIT | TTL beyond the 300s cap. | Reject; tokens cannot exceed 300s. |
A token is also single-use. A connector rejecting a replay (nonce already consumed) returns a rejected execution result with the nonce-consumption reason — do not retry the same token; obtain a new decision.
Rate limiting#
| Status | Code | Cause | What to do |
|---|---|---|---|
429 | RATE_LIMITED | Per-IP limit exceeded (global 500/min, stricter on auth endpoints). | Back off with exponential delay; retry. |
Retry strategy#
Retry only transient failures. Use exponential backoff with jitter for 429 and 5xx; never retry 400 / 401 / 403 unchanged.
Retryable vs terminal#
400,401,403,404— terminal. Fix the request (a403 DENIEDdecision is a correct, terminal outcome).429— retryable. Back off.500,502,503— retryable. Transient. (500 AUTHORITY_LOOP_FAILEDis fail-closed: the action did not happen.)
Next steps#
- API Quickstart — the intent body and decision outcomes.
- API Authentication — the full auth-error table.
- Verify Decision Tokens — token failure reasons in depth.
- Troubleshooting Index — broader issue catalog. </content>