> ## Documentation Index
> Fetch the complete documentation index at: https://devs.surferseo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Read a v2 error response and know what to change.

Every error Surfer handles is JSON in one envelope, whatever the endpoint returns on success: the status gives the class of failure and `error.reason` names the cause. Find the status and reason in the [table below](#reasons-and-what-to-do), then read `error.details` for the exact field or lookup that failed.

An unhandled `500` falls outside the envelope: a JSON request gets `{"errors": {"detail": "Internal Server Error"}}`, and a request that asked for HTML gets an HTML error page.

## The error envelope

Match errors by HTTP status and `error.reason`; treat `error.message` as optional context.

A `PATCH` to a Content Editor's SEO Guidelines terms that would raise the custom terms above 500 answers `400` with this body:

```json theme={"system"}
{
  "error": {
    "reason": "validation_error",
    "message": "Custom terms limit exceeded",
    "details": [
      {
        "field": "terms",
        "reason": "too_many",
        "message": "Maximum 500 custom terms allowed"
      }
    ]
  }
}
```

| Key | Present | Content |
| - | - | - |
| `reason` | Always | One of the fixed keys in the table below. Match on it and the status in code. |
| `details` | Always | A list of objects whose shape depends on `reason`; empty when there is nothing to add. |
| `message` | Sometimes | Context written for people. Log it or show it; it is not a key to match on. |

## Reasons and what to do

Find the response's status and reason below to choose the next action.

| Status | Reason | Meaning | What to do |
| - | - | - | - |
| `400` | `validation_error` | A parameter, header, or body value failed validation, or the body is not valid JSON. | Fix every item in `details` ([Validation details](#validation-details)). |
| `401` | `unauthorized` | The request was not authenticated. | See [When authentication fails](/api/authentication#when-authentication-fails). |
| `403` | `permission_denied` | The key is valid but the request is not permitted. | See [When authentication fails](/api/authentication#when-authentication-fails). |
| `404` | `not_found` | The workspace, the resource, or the route does not exist for your key. | Read `details` ([Not-found details](#not-found-details)). |
| `406` | `not_acceptable` | The `Accept` header allows none of the formats the endpoint returns. | Send an `Accept` value from `details[0].allowed`. |
| `409` | `conflict` | The resource is in the wrong state for the request, or a request with the same `Idempotency-Key` is still running. | Read `message` and follow the list under this table. |
| `413` | `content_too_large` | The content document is larger than the endpoint accepts. | Send a shorter document; [Requests and responses](/api/requests#headers-and-formats) states the limit. |
| `415` | `unsupported_media_type` | The `Content-Type` header is not one the endpoint accepts. | Send a `Content-Type` from `details[0].allowed`. |
| `422` | `unprocessable_entity` | The request was valid but Surfer could not carry it out in the current state. | Read `message` and follow the list under this table. |
| `422` | `quota_exceeded` | Your organization has used up the credits or the count allowance the operation needs. | See [Credits](/api/rate-limits#credits). |
| `429` | `rate_limit_exceeded` | Too many requests in the current window. | Wait, then retry; [Over the limit](/api/rate-limits#over-the-limit) has the headers and the retry contract. |
| `500` | `internal_error` | Surfer failed while handling the request. | Before repeating a create or generation request, check whether it started work ([Idempotent creates](/api/requests#idempotent-creates)); if the error repeats, follow [Troubleshooting](/api/troubleshooting). |

Four messages carry their own remedy:

* `409` with `Content editor must be in completed state`: wait until the editor is `completed` ([Content Editor states](/api/async-and-webhooks#content-editor-states)). The capitalization of this message varies between endpoints, so match on the status and reason, not on the text.
* `409` with `A request with this idempotency key is currently being processed`: wait for the first request to finish ([Idempotent creates](/api/requests#idempotent-creates)).
* `422` with `This idempotency key was already used with a different request`: send a new key ([Idempotent creates](/api/requests#idempotent-creates)).
* `422` with `The AI writing system is overloaded, please retry shortly`: wait, then send the request again ([Create AI Article](/api/v2/ai-articles/create-ai-article)).

## Validation details

Each item in the `details` of a `validation_error` names one problem with `field` and `reason`; fix all of them before you resend.

`field` names what failed:

* A body key or query parameter by name, for example `keywords` or `page_size`.
* A nested key as `parent.child`.
* A list position as `parent.0.child` in schema errors, and as `parent[0].child` in content-template and custom-voice body errors.
* `filter[state]` for a rejected filter value.
* `body` when the request body is missing or too large.
* `base` when the problem concerns the request as a whole.

The common `reason` values:

| Reason | Meaning |
| - | - |
| `required` | The field is blank. |
| `missing_field` | A required field, or the request body, is absent. |
| `invalid` | The value has the wrong type or is not one of the allowed values. |
| `invalid_format` | The value does not match the format the field expects. |
| `taken` | The value is already in use. |
| `too_many` | A list has more items than the endpoint allows. |
| `too_large` | The body is over the size limit. [Submit an edited outline](/api/v2/ai-articles/submit-edited-ai-article-outline) answers this way; the content endpoint answers `413` instead. |

Schema errors name the failed check in `reason` and carry no `message` of their own; the envelope's `message` describes every failure in one string. Other details carry a `message` when the check has more to say than the reason, as `Maximum 500 custom terms allowed` does above.

A rejected `sort` or `order` value adds `value`, `allowed`, and `default` to the detail, so the response tells you what to send instead. An unknown `order` produces this detail:

```json theme={"system"}
{
  "field": "order",
  "reason": "invalid",
  "value": "up",
  "allowed": ["asc", "desc"],
  "default": "desc"
}
```

A rejected filter value repeats the offending value in `value`, and a body that is not valid JSON answers with the message `Malformed request body` and empty `details`. [Requests and responses](/api/requests) has the list and body conventions themselves.

## Not-found details

`details` says which lookup failed, so you can tell a wrong workspace from a wrong path.

| Details | Meaning | What to do |
| - | - | - |
| `[{ "reason": "entity_not_found", "entity": "workspace" }]` | The workspace in the path is not available to your key. | Pick an `id` from [List workspaces](/api/v2/workspaces/list-workspaces); [Workspaces in the path](/api/requests#workspaces-in-the-path) has the workspace rule. |
| `[{ "reason": "entity_not_found", "entity": "content_editor" }]` | No Content Editor with that id exists in the workspace. | Check the id against [List Content Editors in workspace](/api/v2/content-editors/list-content-editors-in-workspace). |
| `[{ "reason": "route_not_found" }]` | No endpoint matches the method and path under `/api/v2`. | Compare the request with the endpoint's reference page. |
| `[]` | The resource named last in the path, such as an AI Article, does not exist there. | Check the id. |

## v1 errors

v1 answers most failures with plain text, not the envelope. Humanizer and AI Detector answer `422` with a JSON `{"error": "..."}` body, and invalid parameters come back as `422` with a JSON:API `errors` array whose items carry `title`, `detail`, and `source.pointer`. [Migrating from v1](/api/migrating-from-v1) covers the other differences.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.