Errors
Every error Lazu returns has three stable identifiers: a Lazu error number
(LZ-3101), a code (insufficient_quota) and a type
(insufficient_quota). The HTTP status is always real; an error is never
sent with 200.
Response shape
OpenAI-compatible routes keep OpenAI's error object and add Lazu's fields next to it. SDKs that only know OpenAI's fields keep working.
{
"error": {
"message": "Wallet balance is too low for this request. Top up to continue.",
"type": "insufficient_quota",
"param": null,
"code": "insufficient_quota",
"lazu": "LZ-3101",
"docs": "https://lazu.ai/docs/errors#lz-3101",
"request_id": "req_lazu_01KSBV4MC6THZ9TCZEM38KPYRX",
"details": {}
}
}Anthropic-compatible routes (/v1/messages) use Anthropic's shape and types:
{
"type": "error",
"error": {
"type": "billing_error",
"message": "Wallet balance is too low for this request. Top up to continue.",
"code": "insufficient_quota",
"lazu": "LZ-3101",
"docs": "https://lazu.ai/docs/errors#lz-3101"
},
"request_id": "req_lazu_01KSBV4MC6THZ9TCZEM38KPYRX"
}| Field | Meaning |
|---|---|
code | What happened. Stable; branch on this. |
type | What to do next (see below). Stable. |
lazu | Lazu error number. Stable; quote it to support. |
message | English sentence for developers. May change; never parse it. |
param | The request field at fault, when there is one. |
details | Structured values, such as field, model or retry_after_seconds. |
request_id | The request this error belongs to. |
docs | Link to the row for this number below. |
Every error response also carries the X-Lazu-Request-Id and
X-Lazu-Error headers.
Types
| Type | HTTP | What to do |
|---|---|---|
invalid_request_error | 400, 413 | Fix the request. Do not retry unchanged. |
authentication_error | 401 | Fix the API key: invalid, expired and disabled keys have their own codes. |
permission_error | 403 | The key or account may not do this: IP, model, vendor or project scope. |
not_found_error | 404 | Wrong path, model or ID. |
insufficient_quota | 429 | Balance or a budget ran out. Top up or raise the budget; retrying will not help. |
rate_limit_error | 429 | Wait for Retry-After, then retry. |
content_policy_violation_error | 400 | The model refused the content. Change the content. |
overloaded_error | 503 | Busy. Retry later with backoff. |
timeout_error | 504 | No answer in time. The outcome is unknown; check before replaying. |
api_connection_error | 502 | The connection dropped. The outcome is unknown; check before replaying. |
api_error | 500, 502 | Lazu or the model failed. Retry idempotent requests; report persistent ones with the request ID. |
On /v1/messages, insufficient_quota is sent as Anthropic's
billing_error, and timeout_error / api_connection_error as api_error.
Retry strategy
Only retry a replay-safe request before any output was received. For 429, distinguish a temporary rate limit from insufficient_quota: insufficient balance requires action, not repeated retries. Respect Retry-After when present; use bounded backoff for transient gateway/upstream failures. SDK automatic retries can create another model invocation when execution is uncertain; configure them deliberately. Gateway failover and client retries are separate decisions.
Streams
Once a stream has started, the HTTP status cannot change. Lazu ends the
stream with the protocol's own error frame carrying the same error object
(data: {"error": {...}} then data: [DONE] for Chat Completions, an
error event for Responses and Messages). Keep any partial output and check
request details before replaying. For 413 request_body_too_large, reduce the
request; for 503 gateway_overloaded, retry later with backoff.
Include the request ID in support tickets
Every Lazu response, success or error, includes:
X-Lazu-Request-Id: req_lazu_01KSBV4MC6THZ9TCZEM38KPYRXPaste it, with the LZ- number, into any support ticket. We can trace the
full request path through routing, upstream call and billing.