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:
{
"errors": [
{
"code": "internal_error",
"detail": "an unexpected error occurred",
"field": ""
}
]
}| Field | Description |
|---|---|
code | Machine-readable error code (e.g. not_found, validation_error, internal_error) |
detail | Human-readable message, safe for display to end users |
field | Optional — 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 theX-Request-IDresponse headermethod— HTTP methodpath— request patherr— the original error (only in server logs)
Example response:
{
"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:
{
"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:
{
"errors": [
{
"code": "validation_failed",
"detail": "invalid request data"
}
]
}400 JSON Parse Errors
Invalid request bodies return a generic message without leaking parser internals:
{
"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.
{
"errors": [
{
"code": "body_too_large",
"detail": "request body exceeds size limit"
}
]
}404 Not Found
{
"errors": [
{
"code": "not_found",
"detail": "product not found"
}
]
}422 Business Logic Errors
Some endpoints return controlled business error messages. These are intentional and safe:
{
"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:
{
"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
- Note the
X-Request-IDheader from the error response - Search server logs for that request ID
- The log entry contains the full error, HTTP method, and path
# 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"}