# Errors

> The error format of the OpenWAF API, its HTTP statuses and every stable error code with its meaning.

Errors answer an HTTP status and a JSON body:

```json
{
  "error": {
    "code": "version_conflict",
    "message": "the configuration changed: the current version is 13"
  },
  "current_version": 13
}
```

| Field | Meaning |
|---|---|
| `error.code` | Stable code: use it in your code. |
| `error.message` | Human explanation; its wording may change. |
| `error.field` | Field that failed validation, when there is one (e.g. `ttl`, `records[3].content`). |

Some codes add fields next to `error`: `current_version` (`version_conflict`), `hostnames` (`missing_certs`), `plan` (`merge_not_equivalent`).

## HTTP statuses

| Status | Meaning |
|---|---|
| `200`, `201` | Success. With `dry_run=1`, `200` carries the plan of the change. |
| `204` | Success without a body. |
| `400` | Malformed request: invalid JSON, unknown field, invalid query parameter. |
| `401` | Missing, invalid, expired or revoked token. |
| `403` | The token's role or limits do not allow the operation. |
| `404` | The resource does not exist or the token cannot see it. |
| `409` | The current state does not allow the change. |
| `412` | `If-Match` does not match the current version. |
| `413` | The body is too large. |
| `422` | Validation error or missing change reason. |
| `428` | The operation requires `If-Match`. |
| `429` | Too many requests: wait the seconds in `Retry-After`. |
| `502`, `503` | A dependency did not answer or the feature is not available: retry later. |

## Error codes

| Code | Status | Description |
|---|---|---|
| `bad_request` | `400` | Malformed request: invalid JSON, unknown field or invalid parameter. |
| `unauthorized` | `401` | Missing, invalid, expired or revoked token. |
| `forbidden` | `403` | The role or the limits of the token do not allow the operation. |
| `token_scope` | `403` | A cache-purge-only token called another route. |
| `feature_disabled` | `403` | The feature (egress) is not enabled for the organization. |
| `zone_limit` | `403` | The organization has reached its zone limit. |
| `org_suspended` | `403` | The organization is suspended; writes are refused. |
| `org_pending_approval` | `403` | The organization is waiting for approval; writes are refused. |
| `not_found` | `404` | The resource does not exist or the token cannot see it. |
| `host_not_in_zone` | `404` | The name is neither the zone nor below it. |
| `conflict` | `409` | The current state does not allow the change (e.g. a disabled zone, the last owner, a duplicate name). |
| `zone_exists` | `409` | The domain is already added. |
| `zone_overlaps` | `409` | The domain is a parent or a subdomain of a zone of another organization or another client. |
| `zone_not_active` | `409` | The zone is not active. |
| `zone_not_external` | `409` | The operation is only for external zones. |
| `zone_merged` | `409` | The zone is merged into another one and is read-only until it is unmerged. |
| `merge_not_equivalent` | `409` | The merge would change how some name is served; the body carries the plan. |
| `merge_not_reversible` | `409` | The zone is not merged or its parent changed since the merge. |
| `missing_certs` | `409` | Some sites would be left without a valid certificate; the body lists the hostnames. |
| `already_member` | `409` | The person is already a member of the organization. |
| `owner_of_zones` | `409` | The change would leave zones without an owner. |
| `client_has_zones` | `409` | The client still has zones; move or delete them first. |
| `host_taken` | `409` | The name is used by another organization. |
| `target_in_use` | `409` | An egress agent still allows the target. |
| `version_conflict` | `412` | `If-Match` does not match the current version; the body carries `current_version`. |
| `too_large` | `413` | The body or the file is too large. |
| `invalid` | `422` | Validation error; `field` names the field. |
| `reason_required` | `422` | The `X-Change-Reason` header is missing or invalid. |
| `if_match_required` | `428` | The operation requires `If-Match`. |
| `rate_limited` | `429` | Too many requests; wait the seconds in `Retry-After`. |
| `too_many_streams` | `429` | Too many event streams open in the organization. |
| `dns_backend` | `502` | The DNS service rejected the change; retry later. |
| `dns_unavailable` | `502` | The public DNS did not answer. |
| `credential_store` | `502` | The credential store refused the write; retry later. |
| `unavailable` | `503` | The feature is not available right now; retry later. |
| `busy` | `503` | Too many checks in progress; retry in a few seconds. |
| `internal` | `500` | Unexpected error; retry, and contact support@openwaf.dev if it persists. |
