Skip to content
WP EngineDocumentation

Errors

The AI API returns standard HTTP status codes. Most error responses share a single JSON envelope. For the exceptions, see other response shapes.

{
"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"
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.

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.

A few responses don’t use the standard envelope. Check the status code first and parse the body defensively.

  • 429 from 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-text 405 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 no code field, 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"
    }
    }

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.


Last updated: