Files
AstraBox/docs/api-errors.md
T
Colton Qi f8db994652 AstraBox 0.1.0
The open-source, self-hosted alternative to Claude Managed Agents: run
Claude Code, Codex, Hermes, DeepSeek Harness and Pi as managed Agents on
your own infrastructure, with conversations that start and resume in
seconds.
2026-09-22 13:24:05 -07:00

8.8 KiB

Errors

Handle the AstraBox error envelope and decide whether a failed request can be retried.

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"
  }
}

Retry when 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

  1. Branch on code and error.retryable, not only on the HTTP status.
  2. Log the traceparent response header, code, and message for diagnostics.
  3. Inspect data, error.evidence, and error.cause_code when present.
  4. Do not retry when error.retryable is false unless a 429 response provides data.retry_after_seconds.
  5. 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.