Errors
The AI API returns standard HTTP status codes. Most error responses share a single JSON envelope. For the exceptions, see other response shapes.
Read the standard error envelope
Section titled “Read the standard error envelope”{ "error": { "message": "model not found: openai/does-not-exist", "type": "not_found_error", "code": 404, "request_id": "9f4dc8fb8841f0b0cc67612b6ef54fb7" }}| Field | Type | Notes |
|---|---|---|
error.message |
string | Human-readable description. |
error.type |
string | Machine-readable category, such as not_found_error. See status codes. |
error.error_code |
string | Optional. A more specific cause, such as terms_required on an entitlement_required refusal. Allow for new values. |
error.code |
integer | Matches the HTTP status code. |
error.request_id |
string | Identifier for this request. The API also returns it in the X-Request-Id response header. Quote it when you contact support. |
error.portal_url |
string | Optional. A User Portal page where you can resolve the error, such as the credit purchase page on a 402. |
Authentication and permission failures use this envelope too. For what
to do about each, see
Authentication.
The API sets the WWW-Authenticate header on 401 responses:
WWW-Authenticate: Bearer realm="ai-services"Handle status codes
Section titled “Handle status codes”| Status | error.type |
Meaning | Retryable |
|---|---|---|---|
400 |
invalid_request_error |
Invalid request body or parameters. | No — fix the request. |
401 |
authentication_error |
Missing, invalid, or revoked API key. | No — check the key. |
402 |
insufficient_credits |
No credits remaining on the account. | No — add credits in the User Portal; error.portal_url links there when set. |
403 |
permission_error |
Valid key, but it lacks the required scope. | No — change the key’s permissions. |
403 |
entitlement_required |
The account can’t use WP Engine AI. error.error_code gives the reason when known: not_eligible, terms_required, not_enabled, or suspended. |
No. For terms_required or not_enabled, an account owner fixes it in the User Portal; error.portal_url links there when set. For suspended or not_eligible, contact support. |
403 |
billing_account_pending |
AI billing isn’t set up for the account yet, usually because the setup that follows turning on AI hasn’t finished. | Yes — retry shortly. |
403 |
account_required |
No billing account is associated with the key. | No. |
404 |
not_found_error |
Unknown or removed model identifier, or the resource doesn’t exist. | No — fix the request. |
413 |
request_too_large |
The request body is larger than the endpoint accepts. | No — send a smaller request. |
429 |
rate_limit_error |
The model provider is busy. Exceeding your own rate limit returns an HTML 429 instead. See rate limits. |
Yes — back off with jitter. |
500 |
internal_error |
Unexpected gateway error. | Yes — retry with backoff. |
502 |
upstream_error |
The model provider returned an error. | Yes — retry with backoff. |
503 |
upstream_error, credits_unavailable, entitlement_unavailable, or model_unpriced |
A service the request depends on is temporarily unavailable. credits_unavailable and entitlement_unavailable name the check that failed; model_unpriced means the model is temporarily unavailable. |
Yes — retry with backoff. |
504 |
timeout_error |
The model provider timed out. | Yes — retry with backoff. |
Some operations can also return 409 or 422.
Handle model provider errors
Section titled “Handle model provider errors”The gateway doesn’t pass a model provider’s error body through. Instead, it returns the standard envelope with a status based on the provider’s status:
| Provider status | Gateway response |
|---|---|
400 |
400 — “the upstream provider rejected the request” |
404 |
404 — “the requested model or resource was not found” |
429 |
429 — “rate limited by the upstream provider” |
408 or 504 |
504 — “the upstream provider timed out” |
| Anything else | 502 — “upstream provider error” |
A provider 429 means the model is busy, not that you exceeded your
own limit. Handle it the same way: back off and retry.
Parse other response shapes
Section titled “Parse other response shapes”A few responses don’t use the standard envelope. Check the status code first and parse the body defensively.
-
429from the load balancer. When you exceed a rate limit, the body is a short HTML page, not JSON. -
Unknown path or method. A path that doesn’t exist returns a plain-text
404 page not found. An unsupported method returns a plain-text405 Method Not Allowed. -
/v1/messages. The Anthropic-compatible endpoint returns errors in Anthropic’s format, so Anthropic software development kits (SDKs) can parse them. The body has nocodefield, so read the status from the response:{"type": "error","error": {"type": "not_found_error","message": "model not found: anthropic/does-not-exist","request_id": "fc0a809a263761c3e07ee97440121445"}}
Handle streaming errors
Section titled “Handle streaming errors”If a streaming response ("stream": true on chat completions) fails
before the first byte, the API returns a normal error response. Once
the stream starts, the status is already 200, so a failure arrives
inside the stream instead. If the gateway loses the model provider’s
stream, it sends an error event followed by [DONE]:
data: {"error":{"message":"stream relay error","type":"server_error"}}
data: [DONE]This frame doesn’t follow the standard envelope: its type is
server_error, and it carries no code or request_id.
Treat a chunk with an error field as a failed response, even though
[DONE] follows it. If the connection closes without data: [DONE],
the stream ended early. Both are recoverable failures, so retry the
request. Stream a chat completion
shows code that handles both.
If the client disconnects mid-stream, the gateway stops relaying and records the usage consumed up to that point.