Errors
layahost uses standard HTTP status codes and the same error bodies as the Jev API, so the Jev SDKs raise their usual exceptions.
Error format
Most errors have a detail object with a machine-readable error_type and a human-readable message:
{
"detail": {
"error_type": "authentication_error",
"message": "Invalid or revoked API key."
}
}
Validation errors (422) use the FastAPI format instead: detail is a list with one entry per problem, and loc is the path to the field. See Validation errors.
Branch on the status code and error_type. Messages are for people and may change.
Status codes
| Status | error_type | Meaning | What to do |
|---|---|---|---|
401 | authentication_error | The API key is missing, invalid or revoked. | Check the Authorization header and the key. Don't retry. |
402 | insufficient_credits | Your prepaid balance doesn't cover this request. | Top up. Don't retry until you have. |
404 | not_found_error | There is no endpoint at this path. | Check the URL: paths start with /v1/, with no /api prefix. |
405 | invalid_request_error | The endpoint exists but not for this HTTP method. | Use POST for /v1/decide and /v1/systemone, GET for the lists. |
422 | (list) | The request body is invalid. | Fix the request. Don't retry. |
429 | rate_limit_error | This key sent too many requests this minute. | Wait for the number of seconds in retry-after, then retry. |
500 | api_error | An unexpected error on our side. | Retry with backoff. If it persists, contact support with the request ID. |
503 | api_error | The inference backend is unavailable or failed. | Retry after retry-after seconds, with backoff. |
529 | overloaded_error | All inference capacity is busy. | Retry after retry-after seconds, with backoff. |
Requests rejected with 401, 402, 404, 405, 422, 429, 503 or 529 are not billed: if a request fails after it was charged, the charge is returned to your balance.
Authentication errors
A missing header and a bad key get different messages:
{
"detail": {
"error_type": "authentication_error",
"message": "Missing API key. Send it as \"Authorization: Bearer <API_KEY>\"."
}
}
See Authentication.
Insufficient credits
layahost is prepaid. When your balance can't cover a request, you get 402 with error_type insufficient_credits and a message with a link to the Billing page. A request is charged as a whole, so a /v1/systemone call with 10 questions needs credit for 10 decisions. We email you before you run out; set the threshold in Settings → Data & alerts. See Pricing & billing.
Unknown endpoints and methods
A path that doesn't exist gets 404, and a known path with the wrong method gets 405:
{
"detail": {
"error_type": "not_found_error",
"message": "Unknown endpoint. See https://layahost.com/docs."
}
}
{
"detail": {
"error_type": "invalid_request_error",
"message": "Method not allowed for this endpoint."
}
}
Validation errors
A body that is missing fields or has values of the wrong kind gets 422. Each entry has loc ("body" followed by the path to the field), msg and type:
{
"detail": [
{
"loc": ["body", "type"],
"msg": "The type field is required when template is not present.",
"type": "value_error"
},
{
"loc": ["body", "question"],
"msg": "The question field is required when template is not present.",
"type": "value_error"
}
]
}
Problems inside a /v1/systemone question also carry the offending input and a ctx object with details, and type names the rule that failed:
{
"detail": [
{
"type": "too_short",
"loc": ["body", "questions", "q", "choice", "criteria"],
"msg": "Dictionary should have at least 2 items after validation, not 1",
"input": {"yes": null},
"ctx": {
"field_type": "Dictionary",
"min_length": 2,
"actual_length": 1
}
}
]
}
Option lists that don't fit the model's option budget (192 tokens for all names and descriptions of one question) also get 422, on ["body", "questions", "<name>", "criteria"] for /v1/systemone and on ["body", "options"] for /v1/decide. See Limits.
Rate limit errors
Each key may send 600 requests per minute. Over that, you get 429 with a retry-after header: the number of seconds until the window resets.
HTTP/1.1 429 Too Many Requests
content-type: application/json
retry-after: 38
x-request-id: req_0t9rn7vlp9c6tl5rgaeetoc8
x-typesafe-request-id: req_0t9rn7vlp9c6tl5rgaeetoc8
{
"detail": {
"error_type": "rate_limit_error",
"message": "Rate limit of 600 requests per minute exceeded."
}
}
See Rate limits.
Server and capacity errors
503 (api_error) means the inference backend was unavailable or failed to answer. 529 (overloaded_error, message “Inference capacity is saturated, retry shortly.”) means all inference capacity is busy. Both are temporary and both come with retry-after: 1:
HTTP/1.1 503 Service Unavailable
content-type: application/json
retry-after: 1
x-request-id: req_guglin9qhthimpisgz5umdhp
x-typesafe-request-id: req_guglin9qhthimpisgz5umdhp
{
"detail": {
"error_type": "api_error",
"message": "Inference backend is unavailable, retry shortly."
}
}
An unexpected error on our side gets 500 with error_type api_error, in the same format.
Retries
- Retry
429,503and529after the number of seconds inretry-after, and back off exponentially if the error repeats. Retry500with backoff. - Don't retry
401,402,404,405or422: the same request will fail again. - Retrying
429,503and529is safe. Those requests were not billed and changed nothing.
The Jev SDKs do this for you. By default, both retry 408, 429 and every 5xx status up to twice, and honour retry-after. There is a fetch-based retry loop in Code examples.
SDK exceptions
| Status | Python (typesafe_sdk) | JavaScript (@typesafe-ai/sdk) |
|---|---|---|
401 | TypeSafeAuthenticationError | AuthenticationError |
402, 405 | TypeSafeAPIError | APIError |
404 | TypeSafeNotFoundError | NotFoundError |
422 | TypeSafeUnprocessableEntityError | UnprocessableEntityError |
429 | TypeSafeRateLimitError | RateLimitError |
500, 503, 529 | TypeSafeInternalServerError | InternalServerError |
Every class in the table extends the SDK's base API error, TypeSafeAPIError or APIError, which has the HTTP status, the parsed body and the request ID (request_id in Python, requestId in JavaScript).
Request IDs
Every response from /v1/, successful or not, carries a request ID such as req_3rmqhpttolpzez0quj0bt42x in two headers with the same value: x-request-id, and x-typesafe-request-id for the Jev SDKs. Successful decisions also include it in the body, as request_id on /v1/decide and meta.request_id on /v1/systemone.
Requests that reach the model are listed in Logs under the same ID. Include the ID when you contact support.