---
title: Errors
description: The error response shape, and what each HTTP status code the AI API returns means.
url: https://developers.wpengine.com/docs/ai/api/errors/
image: https://developers.wpengine.com/og/docs/ai/api/errors.png
---

# Errors

The AI API returns standard HTTP status codes. Most error responses share a single JSON envelope. For the exceptions, see [other response shapes](https://developers.wpengine.com/docs/ai/api/errors/#parse-other-response-shapes).

## Read the standard error envelope

```json
{
  "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](https://developers.wpengine.com/docs/ai/api/errors/#handle-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](https://developers.wpengine.com/docs/ai/api/authentication/#handle-a-rejected-key). The API sets the `WWW-Authenticate` header on `401` responses:

```http
WWW-Authenticate: Bearer realm="ai-services"
```

## 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](https://developers.wpengine.com/docs/ai/api/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

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

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](https://developers.wpengine.com/docs/ai/api/rate-limits/), 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:

  ```json
  {
    "type": "error",
    "error": {
      "type": "not_found_error",
      "message": "model not found: anthropic/does-not-exist",
      "request_id": "fc0a809a263761c3e07ee97440121445"
    }
  }
  ```

## 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]`:

```text
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](https://developers.wpengine.com/docs/ai/api/stream-chat-completions/) shows code that handles both.

If the client disconnects mid-stream, the gateway stops relaying and records the usage consumed up to that point.

---

> #### Handle failure
>
> - [Authentication — what a 401 and a 403 each mean for your key](https://developers.wpengine.com/docs/ai/api/authentication/#handle-a-rejected-key)
> - [Rate limits — quotas, the 429 response, and backoff](https://developers.wpengine.com/docs/ai/api/rate-limits/)

## Was this helpful?

Thanks for the feedback!

```json
{"@context":"https://schema.org","@graph":[{"@type":"WebSite","@id":"https://developers.wpengine.com/#website","name":"WP Engine Product Documentation","url":"https://developers.wpengine.com/","description":"Resources for developing, deploying, and scaling on WP Engine.","inLanguage":"en-US","keywords":["API documentation","product documentation","headless WordPress","managed WordPress hosting"],"publisher":{"@id":"https://wpengine.com/#organization"}}]}
```
