Errors
Every error code this API can answer with, the HTTP status that carries it, and what to do about it. The envelope is OpenAI-shaped with one namespaced addition.
The envelope
error.message is written for a person reading a log, error.type names the class of failure, and error.code is the stable identifier to branch on — message text is not a contract. Where the API has structured detail (which policy, which endpoint, which model) it arrives in error.llmeu and never replaces the three standard fields.
{
"error": {
"message": "No endpoint satisfies residency eu-owned. Residency is never relaxed silently.",
"type": "conflict_error",
"code": "no_endpoint_for_policy",
"llmeu": { "requested": "llmeu-auto" }
}
}
type follows status
The mapping below is the one the API applies, rendered from the same table that builds its responses.
| HTTP | type |
|---|---|
| 400 | invalid_request_error |
| 401 | authentication_error |
| 402 | insufficient_quota |
| 403 | permission_error |
| 404 | not_found_error |
| 409 | conflict_error |
| 413 | invalid_request_error |
| 422 | invalid_request_error |
| 429 | rate_limit_error |
| 500 | api_error |
| 501 | api_error |
| 502 | api_error |
| 503 | api_error |
Codes
Every code that can reach a client. A test walks the API source and fails when this table and the code disagree in either direction, so a code cannot be invented here or added there without being documented.
| HTTP | code | What it means, and what to do |
|---|---|---|
| 400 | invalid_request | The request body failed validation. |
| 400 | invalid_json | The body was not valid JSON. |
| 400 | invalid_llmeu | The llmeu object was not an object, or carried an unknown field. |
| 400 | invalid_residency | The requested residency is not one of the four hosting classes. |
| 400 | invalid_data_class | The requested data_class is not one of the four classes. |
| 400 | invalid_retention | The requested retention is not zero, 24h, 7d or 30d. |
| 400 | invalid_max_tokens | max_tokens was not a positive integer. |
| 400 | invalid_stream | stream was not a boolean. |
| 400 | invalid_stop | stop was neither a string nor an array of strings. |
| 400 | invalid_role | A message carried a role outside system, user, assistant and tool. |
| 400 | invalid_tools | tools or tool_choice was malformed. |
| 400 | missing_model | The model field is required. |
| 400 | missing_messages | messages is required and must not be empty. |
| 400 | missing_input | input is required for embeddings. |
| 400 | missing_name | name is required. |
| 401 | invalid_api_key | The key is missing, unknown, revoked or expired. |
| 402 | budget_exceeded | The organization's daily budget is spent. Raise max_usd_per_day or wait for the window to roll. |
| 403 | api_key_required | The request used a console session. Create an API key and send that instead. |
| 403 | insufficient_scope | The key is valid but lacks the scope this endpoint requires. |
| 403 | no_organization | The credential is not attached to an organization. |
| 403 | model_denied | The organization's policy denies this model or its provider. |
| 403 | policy_violation | The request violates a rule of the organization policy. |
| 403 | retention_exceeds_policy | The requested retention is longer than the policy allows. This is checked before anything else about the request. |
| 403 | data_class_not_allowed | The request's data_class is not one the policy allows. |
| 404 | model_not_found | No such model for this organization. |
| 404 | hosted_unavailable | The model is in the catalogue but not hosted on LLM EU, so it cannot be called. |
| 404 | not_found | The route exists but the object does not. |
| 404 | unknown_route | No route is mounted at this path. Every OpenAI endpoint that is not listed under Endpoints answers this. |
| 404 | policy_not_found | The policy_id in llmeu does not belong to this organization. |
| 409 | no_endpoint_for_policy | Nothing satisfies the policy and the residency together, and neither is relaxed silently. |
| 409 | not_a_text_model | The named model cannot generate text: it is a speech-recognition, embedding or reranker model. |
| 409 | conflict | The request conflicts with the current state of the object. |
| 413 | request_too_large | The body exceeded the endpoint's size limit. |
| 429 | rate_limit_exceeded | A limit from the rate-limit table was reached. The message names the window reset time. |
| 500 | internal_error | An unexpected failure on our side. |
| 501 | not_implemented | The endpoint is intentionally not offered, or the requested capability does not exist on this deployment. |
| 501 | retention_not_implemented | The policy allows a retention other than zero and only zero is implemented, so the request is refused instead of being stored. |
| 501 | unsupported_n | n greater than 1 is not supported. |
| 502 | upstream_error | No endpoint could serve the request after failover. The trace records every attempt. |
| 502 | stream_failed | The stream failed after it had already started. |
no_endpoint_for_policy is the product working
A 409 with this code means the policy and the request together admit no endpoint, and the router refused to relax either one silently. It is not transient: retrying without changing the request, the policy or the residency fails the same way. The error carries the requested residency, and the trace records why each endpoint was rejected.
Errors inside a stream
A streaming request that fails after the headers are sent writes the error as a final SSE frame and then the terminator, so a client that follows the format sees a failure instead of a truncated success.
data: {"error":{"message":"No endpoint could start the stream.","type":"api_error","code":"upstream_error"}}
data: [DONE]