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

# Error responses

> Every failure returns the same JSON envelope with a status code, message, path and timestamp. Validation failures return 400 with the failing fields.

Every failure returns the same JSON envelope, whatever produced it. A single exception filter builds it, so a Prisma error, a guard rejection and a validation failure all look alike to a client.

## The envelope

```json theme={null}
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Resource not found",
  "path": "/apps/cl123",
  "timestamp": "2026-07-30T09:12:44.118Z"
}
```

| Field        | Type   | Meaning                                                                           |
| ------------ | ------ | --------------------------------------------------------------------------------- |
| `statusCode` | number | The HTTP status, repeated in the body so a logged payload is self describing      |
| `error`      | string | The status name, such as `Bad Request`                                            |
| `message`    | string | A human readable explanation. Multiple validation messages are joined with commas |
| `path`       | string | The request path that failed                                                      |
| `timestamp`  | string | UTC ISO 8601 timestamp of the failure                                             |

The shape is `ApiErrorEnvelope` in `@asobeast/shared`, so a TypeScript client can import it rather than redeclare it.

## Status codes

| Status | What causes it in this API                                                                                          |
| ------ | ------------------------------------------------------------------------------------------------------------------- |
| `400`  | A DTO failed validation, or a store URL could not be parsed                                                         |
| `401`  | The session cookie or personal API token is missing, malformed or revoked                                           |
| `402`  | The account is not entitled. Only reachable when `BILLING_ENABLED` is `true`                                        |
| `404`  | The resource does not exist, or an admin surface is refusing to confirm it exists                                   |
| `409`  | A unique constraint was violated, or an AI endpoint was called with `OPENAI_API_KEY` unset                          |
| `500`  | An unhandled exception. The message is generic and the stack is logged server side                                  |
| `501`  | A store that asobeast does not support. Both live stores are supported, so this path stays wired for a future store |
| `502`  | A store request failed upstream, or the web proxy could not reach the API                                           |
| `503`  | The health endpoint could not reach the database                                                                    |
| `504`  | The web proxy timed out waiting for the API, bounded by `API_PROXY_TIMEOUT_MS`                                      |

Two of these are worth reading twice.

A `504` is produced by the Next.js proxy rather than by the API, so it appears when the API is slow rather than when a request is invalid. The same proxy returns `502` when it cannot reach the API at all. See [Web app configuration](/configuration/web).

A `404` on `/docs` or `/admin/queues` is a deliberate answer rather than a missing route. See [The queue dashboard and the OpenAPI surface](/security/admin-surfaces).

## Validation errors

Every controller input is a DTO validated by a global pipe with whitelisting on. Two consequences follow.

* An unknown property is stripped rather than accepted, so a typo in a field name is silently ignored rather than misapplied.
* A failing field produces a `400` whose `message` names the field and the rule it broke.

```json theme={null}
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "country must be a string, country must match /^[a-z]{2}$/ regular expression",
  "path": "/apps/cl123/keywords",
  "timestamp": "2026-07-30T09:14:02.551Z"
}
```

Because unknown properties are stripped rather than rejected, a request that appears to succeed but changed nothing is usually a misspelled field.

## Related

<CardGroup cols={2}>
  <Card title="API conventions" icon="ruler" href="/api-reference/conventions">
    What the successful responses look like.
  </Card>

  <Card title="Troubleshooting" icon="life-buoy" href="/operations/troubleshooting">
    The same codes seen from the operator side.
  </Card>
</CardGroup>
