Errors
The error format, every code with its cause and fix, retrying, errors inside a stream and what to send to support.
The error format
Every failed /v1 request returns a JSON body with a single error object, whatever the status, and the response always carries an x-request-id header:
{ "error": { "message": "'temperature' must be between 0 and 2.", "type": "invalid_request_error", "param": "temperature", "code": "invalid_value", "request_id": "6f1d0c52-8a0e-4b0f-9d57-3c1a7a2f4e10" }}
| Field | Meaning |
|---|---|
code | A stable, machine-readable reason. Branch on this in your code. |
type | The group of the error: invalid_request_error (your request is wrong), authentication_error (the key), insufficient_quota (credit), permission_error (your plan or the content), rate_limit_error (too many requests) or api_error (a problem on our side or the model's). |
message | A sentence for people, in English. It may be reworded, so never match on it. |
param | The request field that caused the error, such as model or messages, or null. |
request_id | The id of this call. It equals the x-request-id header. |
Some errors add a field: wallet_balance_units on 402 (your balance), quota_code on quota_exceeded and suggested_models on model_does_not_support_images.
Three more typical responses:
{"error": {"message": "Missing credentials. Send 'Authorization: Bearer <your Hoveydai API key>'.", "type": "authentication_error", "param": null, "code": "invalid_api_key", "request_id": "..."}}{"error": {"message": "Rate limit reached (60 per minute). Retry after 3 seconds.", "type": "rate_limit_error", "param": null, "code": "rate_limit_exceeded", "request_id": "..."}}{"error": {"message": "The model is temporarily unavailable or busy. Please retry shortly.", "type": "api_error", "param": null, "code": "model_unavailable", "request_id": "..."}}Every error code
| Status | Code | What happened | Usual cause | What to do |
|---|---|---|---|---|
400 | invalid_json | The body could not be read as a JSON object. | An empty body, a trailing comma, or a body that is not JSON. | Check the body with jq . and send Content-Type: application/json. |
400 | missing_required_parameter | A required field is absent. | model, messages or a message's role or content is missing. | Add the field named in param. |
400 | invalid_type | A field has the wrong type. | For example messages is a string, or stream is "true" instead of true. | Send the type the message asks for. |
400 | invalid_value | A field is present but its value is not allowed. | temperature outside 0 to 2, top_p outside 0 to 1, an empty messages list, more than 4 stop strings. | Bring the value in range, as the message states. |
400 | unsupported_value | The value is valid in general but not supported here. | n other than 1, or a role that is not system, developer, user or assistant. | Use n: 1 and one of the supported roles. |
400 | invalid_image_url | An image part could not be used. | A remote http(s) URL, bad base64, a file over 20 MB or a type other than PNG, JPEG, WebP and GIF. | Send the image inline as data:image/png;base64,... within these limits. |
400 | too_many_images | The request holds more than 8 images. | Many image_url parts in one request. | Split them over several requests. |
401 | invalid_api_key | The key is missing, wrong or deleted. | No Authorization header, a typo, or a key you deleted. | Send Authorization: Bearer hvd_... with a key that is listed under Keys in the dashboard. |
401 | token_expired | A dashboard session token has expired. | Calling the API with a login token instead of a key. | Use an API key, or sign in again. |
401 | session_revoked | The dashboard session was signed out. | Signing out elsewhere invalidated the token. | Use an API key, or sign in again. |
402 | insufficient_credits | Your balance cannot cover this request. | The model is paid and the wallet is empty (free models never need a balance). | Add credit to the wallet or upgrade the plan; wallet_balance_units shows what is left. |
402 | quota_exceeded | A plan limit is reached. | The daily or monthly image allowance; quota_code says which. | Wait for the limit to reset or upgrade the plan. |
403 | model_requires_paid_plan | This model is not on your plan. | A free plan calling a paid-only model. | Pick another model, upgrade, or add credit to the wallet. |
403 | content_blocked | The model's content filter refused the request. | The prompt or the file triggered the filter. | Change the input; resending it unchanged gives the same result. |
404 | model_not_found | There is no such model for you. | A typo in model, a model removed from the list, or one you cannot use. | Copy an id from GET /v1/models, or use auto. |
404 | unknown_url | No such address under /v1. | A wrong path, such as /v1/embeddings. | Use POST /v1/chat/completions or GET /v1/models. |
405 | method_not_allowed | The path exists, but not with this HTTP method. | GET on /v1/chat/completions, for example. | Use the method in the Allow header of the response. |
413 | request_too_large | The body is bigger than 40 MB. | Large base64 images. | Shrink or drop images, or send fewer messages. |
422 | context_length_exceeded | The input and the requested output do not fit the context. | A long history, or a large max_tokens. | Drop old messages, shorten the input, or lower max_tokens. |
422 | model_does_not_support_images | The model cannot read images. | An image_url part sent to a text-only model. | Use a model from suggested_models, or one with supports_vision in /v1/models. |
429 | rate_limit_exceeded | Your key sent more requests than its per-minute limit. | A loop or a burst; the default limit is 60 requests a minute per key. | Wait Retry-After seconds, then retry; spread requests out. |
429 | too_many_concurrent | Too many of your requests are running at the same moment. | By default 2 at once on the free plan and 4 on paid plans. | Wait for one to finish, or retry after Retry-After. |
500 | internal_error | Something failed inside Hoveydai. | A bug on our side. | Retry once; if it repeats, write to support with the request_id. |
502 | provider_error | The model's provider failed or returned an empty answer. | An upstream fault. | Retry with backoff; if it persists, write to support. |
503 | model_unavailable | The model is busy or temporarily down. | Heavy load on the provider. | Retry after Retry-After, or use another model. |
503 | service_unavailable | The service cannot take requests right now. | The catalog is not ready, or the provider side is being fixed. | Retry after Retry-After. |
504 | provider_timeout | The model took too long to answer. | A long answer or a slow provider. | Retry, ask for a shorter answer, or use stream: true. |
Which errors to retry
Retry only 429, 502, 503 and 504. Every other 4xx repeats itself until you change the request, and 500 deserves one retry at most.
- If the response has a
Retry-Afterheader, wait that many seconds. It is present on429and503. - Otherwise wait 1 second, then 2, 4 and 8, and stop after 4 or 5 attempts. Add a little randomness so many clients do not retry together.
for wait in 1 2 4 8; do code=$(curl -s -o reply.json -w "%{http_code}" https://api.hoveydai.ir/v1/chat/completions \ -H "Authorization: Bearer $HOVEYDAI_API_KEY" -H "Content-Type: application/json" \ -d '{"model": "MODEL_ID", "messages": [{"role": "user", "content": "Hello"}]}') case $code in 429|502|503|504) sleep $wait ;; *) break ;; esacdonecat reply.json
Errors in a streaming response
What happens depends on when the failure comes:
- Before the first chunk (a bad key, an unknown model, no credit, a provider that fails at once): you get an ordinary HTTP error with the status and JSON body described above. Handle it as for a request without
stream. - After the first chunk: the HTTP status is already
200, so the error arrives as one more event,data: {"error": {...}}, with the same fields. The stream then ends withoutdata: [DONE]. Keep the text received so far, check each event for anerrorkey, and decide whether to retry the whole request.
A connection that is cut with no error event also means an unfinished answer: a stream is complete only after data: [DONE].
Rate limit headers
Responses to a rate-limited key carry these headers, and a browser script can read them too:
| Header | Meaning |
|---|---|
x-ratelimit-limit-requests | The requests allowed in the current window. |
x-ratelimit-remaining-requests | How many are left in this window. |
x-ratelimit-reset-requests | Time until the window resets, such as 42s. |
Retry-After | On 429 and 503: the seconds to wait before retrying. |
The x-ratelimit-* headers can be absent on some deployments, so do not depend on them. Rely on 429 and Retry-After.
What to send to support
Write to support@hoveydai.ir and include:
- The
request_idfrom the error body, or thex-request-idheader. It lets us find the exact call. - The time of the call and your time zone.
- The
modelid you used. - The status and
codeyou received. Never include your API key.
Did this page not help? Tell us.