Errors
The AstraBox API returns errors in a consistent envelope. Each error response carries structured fields suitable for programmatic handling and debugging.
Error envelope
AstraBox REST resource handlers use this JSON structure:
{
"code": "SESSION_BUSY",
"message": "session is busy",
"data": null,
"error": {
"code": "SESSION_BUSY",
"status_code": 409,
"category": "state",
"retryable": true,
"owner": "session",
"user_message": "session is busy"
}
}
Every response also carries a W3C traceparent header. Unexpected server errors
include the same trace ID in data.trace_id so an operator can correlate the
response with logs.
Field descriptions
| Field | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Stable error code for programmatic handling |
message | string | Yes | Message safe to show to the caller |
data | any | null | Yes | Error-specific structured data, when available |
error.code | string | Yes | Same stable error code as the top-level code |
error.status_code | integer | Yes | Status registered for the error code; use the actual HTTP response status for transport handling |
error.category | string | Yes | Error category, such as request, auth, state, or persistence |
error.retryable | boolean | Yes | Whether retrying after a delay can succeed without changing the request |
error.owner | string | Yes | Component or party that must act: client, session, mongo, runtime, template, platform, or unknown |
error.user_message | string | Yes | Message safe to show to the caller |
error.debug_message | string | No | Additional diagnostic message when the error provides one |
error.evidence | object | No | Structured evidence for diagnosis |
error.cause_code | string | No | Lower-level cause code when one is available |
Error types
Error codes describe the specific failure; HTTP status describes how the request completed. Common status groups are:
| HTTP status | Example code | Description |
|---|---|---|
| 400 or 422 | INVALID_REQUEST | Invalid or missing request parameters |
| 401 | AUTH_REQUIRED, UNAUTHORIZED, TOKEN_EXPIRED | Authentication failed or is required |
| 403 | FORBIDDEN, API_TOKEN_SCOPE_INSUFFICIENT | Authenticated but not authorized for the operation |
| 404 | NOT_FOUND, SESSION_NOT_FOUND | Target resource is missing or inaccessible |
| 409 | SESSION_BUSY, IDEMPOTENCY_KEY_CONFLICT | Resource state conflicts with the operation |
| 429 | ADMISSION_DENIED | Deployment admission policy refused the work |
| 499 | REQUEST_CANCELLED | The caller cancelled the request |
| 5xx | PERSISTENCE_UNAVAILABLE, UNEXPECTED_SERVER_ERROR | AstraBox, infrastructure, or an upstream dependency failed |
The table contains examples, not a complete error-code catalog. Use the
deployed release's /openapi.json and the fields returned by the failing route.
Error type details
400 or 422 — INVALID_REQUEST
The request format or parameters are invalid.
Common triggers:
- Missing required field (for example,
permission_mode) - Field type mismatch (such as a number where a string is expected)
- Parameter outside the accepted range
- Malformed JSON
{
"code": "INVALID_REQUEST",
"message": "permission_mode is required",
"data": null,
"error": {
"code": "INVALID_REQUEST",
"status_code": 400,
"category": "request",
"retryable": false,
"owner": "client",
"user_message": "permission_mode is required"
}
}
# Example trigger: missing the permission_mode field
curl --fail-with-body --silent --show-error \
-X POST "$SERVICE_URL/api/v1/sessions/$SESSION_ID/permission-mode" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
401 — authentication errors
Authentication failed.
Common triggers:
- Missing credential on a protected route
- Malformed or invalid bearer token
- Expired or revoked Access Token
- Credential issued by an untrusted issuer
{
"code": "TOKEN_EXPIRED",
"message": "bearer token expired; obtain a new one from your token issuer",
"data": null,
"error": {
"code": "TOKEN_EXPIRED",
"status_code": 401,
"category": "auth",
"retryable": false,
"owner": "client",
"user_message": "bearer token expired; obtain a new one from your token issuer"
}
}
# Example trigger: invalid token
curl --fail-with-body --silent --show-error \
"$SERVICE_URL/api/v1/agents" \
-H "Authorization: Bearer invalid-token"
403 — authorization errors
The caller is authenticated but not authorized.
Common triggers:
- The identity cannot manage the target Agent
- The Access Token does not include the scope required for the operation
- A non-administrator calls an administration route
{
"code": "API_TOKEN_SCOPE_INSUFFICIENT",
"message": "API token requires scope astrabox:admin",
"data": { "required_scope": "astrabox:admin" },
"error": {
"code": "API_TOKEN_SCOPE_INSUFFICIENT",
"status_code": 403,
"category": "auth",
"retryable": false,
"owner": "client",
"user_message": "API token requires scope astrabox:admin"
}
}
# Example trigger: a read-only token calls an administration API
curl --fail-with-body --silent --show-error \
"$SERVICE_URL/api/v1/admin/environments" \
-H "Authorization: Bearer $READ_TOKEN"
404 — not-found errors
The target resource does not exist or is not visible to the caller.
Common triggers:
- Agent, Session, or Environment ID does not exist
- The resource was deleted
- The caller is not authorized to discover another owner's resource
- URL path typo
{
"code": "SESSION_NOT_FOUND",
"message": "session not found",
"data": null,
"error": {
"code": "SESSION_NOT_FOUND",
"status_code": 404,
"category": "request",
"retryable": false,
"owner": "session",
"user_message": "session not found"
}
}
# Example trigger: nonexistent Session
curl --fail-with-body --silent --show-error \
"$SERVICE_URL/api/v1/sessions/session_nonexistent_123" \
-H "Authorization: Bearer $ACCESS_TOKEN"
409 — conflict errors
Resource state conflict prevents the operation.
Common triggers:
- Same idempotency key reused for a different conversation
- Session is already processing another turn
- Agent or Environment state does not allow the requested operation
{
"code": "SESSION_BUSY",
"message": "session is busy",
"data": null,
"error": {
"code": "SESSION_BUSY",
"status_code": 409,
"category": "state",
"retryable": true,
"owner": "session",
"user_message": "session is busy"
}
}
5xx — server and dependency errors
AstraBox, its infrastructure, or an upstream dependency failed.
Common triggers:
- Database unavailable
- Sandbox or Agent program failed to start
- Identity provider or model gateway unavailable
- Unexpected internal failure
{
"code": "PERSISTENCE_UNAVAILABLE",
"message": "mongodb timeout/unavailable, please retry",
"data": null,
"error": {
"code": "PERSISTENCE_UNAVAILABLE",
"status_code": 503,
"category": "persistence",
"retryable": true,
"owner": "mongo",
"user_message": "mongodb timeout/unavailable, please retry"
}
}
error.retryable is true, or when a 429 response provides
data.retry_after_seconds. Use bounded exponential backoff and wait at least
the provided number of seconds.
Error handling best practices
- Branch on
codeanderror.retryable, not only on the HTTP status. - Log the
traceparentresponse header,code, andmessagefor diagnostics. - Inspect
data,error.evidence, anderror.cause_codewhen present. - Do not retry when
error.retryableisfalseunless a429response providesdata.retry_after_seconds. - Use bounded exponential backoff for retryable responses.
# Request with error handling
headers=$(mktemp)
trap 'rm -f "$headers"' EXIT
response=$(curl --silent --show-error -D "$headers" -w "\n%{http_code}" \
"$SERVICE_URL/api/v1/agents" \
-H "Authorization: Bearer $ACCESS_TOKEN")
http_code=$(echo "$response" | tail -1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" -ge 400 ]; then
error_code=$(echo "$body" | python3 -c "import sys,json; print(json.load(sys.stdin)['code'])")
retryable=$(echo "$body" | python3 -c "import sys,json; print(json.load(sys.stdin)['error']['retryable'])")
traceparent=$(sed -n 's/^[Tt]raceparent: //p' "$headers" | tr -d '\r')
echo "API error: $error_code retryable=$retryable traceparent=$traceparent"
fi
Next steps
- Overview — how AstraBox fits together.