Errors.
The full set of error codes Aria returns today, and the JSON envelope planned for retrieval endpoints rolling out next.
Chat errors are plain text
The chat and demo endpoints return errors as plain text with the appropriate HTTP status. This is intentional during the streaming-era transport; retrieval endpoints (planned) use a structured JSON envelope described below.
Structured envelope (retrieval endpoints)
{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Try again in 23 seconds.",
"requestId": "req_01HW…",
"retryAfterSec": 23,
"fieldErrors": null
}
}code is the machine-readable identifier from the table below. message is a human-readable explanation. requestId echoes X-Request-Id. retryAfterSec is only populated on rate-limit errors. fieldErrors is only populated on VALIDATION_FAILED.
Error codes
| Code | HTTP | Where | Description |
|---|---|---|---|
INVALID_JSON | 400 | all | Request body could not be parsed as JSON. |
MISSING_MESSAGES | 400 | chat + demo | `messages` missing, empty, or not an array. |
LAST_MESSAGE_NOT_USER | 400 | chat | Final message in the array does not have role "user". |
VALIDATION_FAILED | 422 | onboarding (planned) | Field-level validation errors; `fieldErrors` populated on the response. |
UNAUTHENTICATED | 401 | live surfaces | No valid session cookie on the request. |
NOT_ONBOARDED | 403 | live surfaces | Signed in, but `User.onboardedAt` is null — redirect to /onboarding. |
CONVERSATION_NOT_FOUND | 404 | conversations (planned) | No conversation exists with that ID for this user's organization. |
RATE_LIMITED | 429 | all | Request exceeded the per-user or per-IP rate limit. See Retry-After header. |
UPSTREAM_OVERLOADED | 503 | chat surfaces | Anthropic reported the model as overloaded. Retry with backoff. |
UPSTREAM_AUTH | 500 | chat surfaces | Model provider authentication misconfigured — this pages on-call. |
UPSTREAM_BAD_REQUEST | 400 | chat surfaces | The model rejected the request shape — usually a client-side payload issue. |
UPSTREAM_ERROR | 502 | chat surfaces | Other model-side error. Retries are safe. |
INTERNAL | 500 | all | Uncaught exception. Request ID is logged; include it when reporting. |
Field-level validation
When a request fails validation, the fieldErrors map is populated with keys that match the request body structure. Example from the planned POST /api/onboarding:
{
"error": {
"code": "VALIDATION_FAILED",
"message": "A few fields need attention.",
"fieldErrors": {
"company.industry": "Choose an industry.",
"company.subIndustry": "Choose a sub-sector."
},
"requestId": "req_01HW…",
"retryAfterSec": null
}
}Retry policy
- RATE_LIMITED (429) — honor
Retry-After. The header is always present on 429. - UPSTREAM_OVERLOADED (503) — retry with exponential backoff: 1s, 2s, 4s, 8s, cap at 30s. Stop after 5 attempts.
- UPSTREAM_ERROR (502) — retry once. If it fails again, surface to the user.
- UPSTREAM_AUTH (500) — do not retry. This pages on-call; the misconfiguration is on our side.
- INTERNAL (500) — do not retry. Include
X-Request-Idwhen reporting. - 4xx (other) — do not retry. The request is structurally wrong.
Correlating with support
Every response — successful or failing — carries X-Request-Id. Include it when you open a ticket. Internal logs correlate it with the upstream Anthropic requestID, which is not exposed on the response.
Next: Rate limits.