Skip to content

Error Handling

Stoa follows a strict error handling policy to prevent information disclosure. Internal error details (database schema, file paths, stack traces) are never exposed to API clients.

Response Format

All error responses use the standard envelope:

json
{
  "errors": [
    {
      "code": "internal_error",
      "detail": "an unexpected error occurred",
      "field": ""
    }
  ]
}
FieldDescription
codeMachine-readable error code (e.g. not_found, validation_error, internal_error)
detailHuman-readable message, safe for display to end users
fieldOptional — the request field that caused the error

Error Categories

500 Internal Server Errors

All internal errors return the generic message "an unexpected error occurred" with code internal_error. The actual error is logged server-side with:

  • request_id — correlates with the X-Request-ID response header
  • method — HTTP method
  • path — request path
  • err — the original error (only in server logs)

Example response:

json
{
  "errors": [
    {
      "code": "internal_error",
      "detail": "an unexpected error occurred"
    }
  ]
}

To debug a 500 error, use the X-Request-ID header value from the response to search the server logs.

400 Validation Errors

Validation errors return structured field-level feedback when possible:

json
{
  "errors": [
    {
      "code": "validation_error",
      "detail": "required",
      "field": "Name"
    },
    {
      "code": "validation_error",
      "detail": "min",
      "field": "PriceNet"
    }
  ]
}

If validation fails in an unexpected way, a generic fallback is returned:

json
{
  "errors": [
    {
      "code": "validation_failed",
      "detail": "invalid request data"
    }
  ]
}

400 JSON Parse Errors

Invalid request bodies return a generic message without leaking parser internals:

json
{
  "errors": [
    {
      "code": "invalid_json",
      "detail": "request body is not valid JSON"
    }
  ]
}

413 Request Entity Too Large

Returned when the request body exceeds the configured size limit (server.max_body_size for JSON, server.max_upload_size for multipart). See Configuration — Request Body Limits.

json
{
  "errors": [
    {
      "code": "body_too_large",
      "detail": "request body exceeds size limit"
    }
  ]
}

404 Not Found

json
{
  "errors": [
    {
      "code": "not_found",
      "detail": "product not found"
    }
  ]
}

422 Business Logic Errors

Some endpoints return controlled business error messages. These are intentional and safe:

json
{
  "errors": [
    {
      "code": "code_invalid",
      "detail": "discount code is not valid"
    }
  ]
}

Examples include discount code validation (code_invalid, max_uses_reached) and order status transitions (invalid_transition).

Bulk Operations

Bulk endpoints (e.g. POST /api/v1/admin/products/bulk) return per-item results with generic error messages:

json
{
  "data": {
    "total": 3,
    "succeeded": 2,
    "failed": 1,
    "results": [
      { "index": 0, "success": true, "id": "..." },
      { "index": 1, "success": false, "errors": ["failed to create product"] },
      { "index": 2, "success": true, "id": "..." }
    ]
  }
}

Item-level errors use generic messages like "failed to create product" or "failed to resolve options". Detailed errors are logged server-side.

Debugging Errors

  1. Note the X-Request-ID header from the error response
  2. Search server logs for that request ID
  3. The log entry contains the full error, HTTP method, and path
bash
# Example log output (structured JSON via zerolog):
{"level":"error","error":"pq: duplicate key value violates unique constraint \"products_sku_key\"","request_id":"abc-123","method":"POST","path":"/api/v1/admin/products","message":"internal server error"}

Released under the APACHE 2.0 License.