Error handling
A practical reference for interpreting failures from the cloro API: the status codes we return, the shape of every error payload, how automatic retries work, and what to check when a request does not behave as expected.
Overview
Every cloro endpoint reports outcomes through conventional HTTP status codes paired with a predictable JSON body. A 2xx code means the request was accepted and processed; a 4xx code points to something in the request itself that needs correcting; and a 5xx code signals a problem on our side or in an upstream provider. Because the envelope is uniform across endpoints, a single handler in your client can parse and branch on any error we send.
HTTP status codes
| Field | Type | Description |
|---|---|---|
| 200 | Success | The request completed and a result was returned. |
| 400 | Bad Request | One or more parameters failed validation. |
| 401 | Unauthorized | The API key was absent, malformed, or no longer valid. |
| 403 | Forbidden | The key lacks the required scope, or the account is out of credits. |
| 404 | Not Found | The requested route does not exist. |
| 409 | Conflict | The operation clashed with the current state of a resource. |
| 429 | Too Many Requests | A concurrency or rate ceiling was reached; which one depends on the endpoint. |
| 499 | Client Closed Request | The caller aborted the connection before the response was sent. |
| 500 | Internal Server Error | An unexpected condition arose while processing the request. |
| 502 | Bad Gateway | An upstream provider returned an error or timed out. |
Error response formats
Most failures arrive as a nested error object carrying a machine-readable code, a human-readable message, an optional details map, and an ISO 8601 timestamp. Validation failures use a flatter shape with a success flag and a per-field details array. Treat the code value as the stable contract; the message text is meant for humans and may be reworded over time.
Standard error
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable explanation of what went wrong",
"details": {
"context": "endpoint-specific fields appear here"
},
"timestamp": "2026-08-14T09:30:00.000Z"
}
}
Validation error (400)
{
"success": false,
"error": "Request validation failed",
"details": [
{
"field": "prompt",
"message": "Prompt cannot be empty"
}
]
}
Canceled request (499)
{
"success": false,
"error": {
"code": "REQUEST_CANCELED",
"message": "Request was canceled by client",
"timestamp": "2026-08-14T09:30:00.000Z"
}
}
Rate limiting (429)
Monitor endpoints under /v1/monitor/* enforce a concurrency ceiling and return CONCURRENT_LIMIT_EXCEEDED. All endpoints under /v1/* additionally enforce a sliding request rate and return RATE_LIMIT_EXCEEDED.
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded",
"details": {
"limit": 500,
"window": "1s"
},
"timestamp": "2026-08-14T09:30:00.000Z"
}
}
Common error codes
| Field | Type | Description |
|---|---|---|
| MISSING_API_KEY | 401 | No API key was supplied on the request. |
| INVALID_API_KEY_FORMAT | 401 | The key did not match the expected format. |
| INVALID_OR_EXPIRED_API_KEY | 401 | The key is unrecognized or has expired. |
| INSUFFICIENT_PERMISSIONS | 403 | The key does not carry the scope this endpoint requires. |
| INSUFFICIENT_CREDITS | 403 | The account balance cannot cover the request. |
| CONCURRENT_LIMIT_EXCEEDED | 429 | Too many monitor requests are in flight at once. |
| RATE_LIMIT_EXCEEDED | 429 | Requests arrived faster than the allowed rate window. |
| EXTERNAL_SERVICE_ERROR | 502 | An upstream AI provider failed to respond correctly. |
| INTERNAL_SERVER_ERROR | 500 | An unexpected server-side fault occurred. |
Retry and cancellation
cloro automatically re-attempts transient failures on the server side, making up to ten attempts before giving up. Retrying stops as soon as the request succeeds or the attempt budget is exhausted, so you rarely need your own timeout logic for momentary glitches. For 500 and 502 responses, layering a client-side retry with exponential backoff on top of our built-in mechanism adds useful resilience against network hiccups and upstream slowdowns.
Notes
Canceling a request after submission does not refund it: you are billed for the work already performed, since charges reflect resources consumed rather than only completed results. Batch task creation follows a partial-success model, so individual entries may fail with per-item codes such as VALIDATION_ERROR or RESOURCE_ALREADY_EXISTS inside an overall 200 response. Prompts must be between 1 and 10,000 characters, country codes must be valid ISO 3166-1 alpha-2 values, and boolean include.* flags reject any non-boolean value. If failures persist after these checks, reach out at [email protected].