# Errors

One error shape everywhere, with a type to branch on and a message to show.

Every error is JSON with the same shape:

```json An error
{
  "type": "invalid_status_transition",
  "message": "A draft campaign cannot become paused. Set it live first."
}
```

- **type** is stable: branch on it.
- **message** is a plain sentence for a person: show it, never parse it.
- **errors** comes with `invalid_data` and lists each field that is wrong.

```json A validation error
{
  "type": "invalid_data",
  "message": "2 fields are not valid: email: Invalid input: expected string, received null; tags: Invalid input: expected array, received undefined.",
  "errors": [
    { "path": "email", "message": "Invalid input: expected string, received null" },
    { "path": "tags", "message": "Invalid input: expected array, received undefined" }
  ]
}
```

## Types

| Status | type | When |
| --- | --- | --- |
| 400 | `invalid_data` | A field is missing or has the wrong form, or a value is not allowed (an unknown time zone, a step that would make a loop) |
| 401 | `unauthorized` | No valid API key, OAuth token or session |
| 402 | `plan_limit_reached` | Your plan’s contacts, monthly emails or projects are used up |
| 404 | `<resource>_not_found` | The id in the path does not exist or belongs to another account, like `campaign_not_found` |
| 409 | `contact_exists` | The address is already a contact in the project |
| 409 | `invalid_status_transition` | That status change is not allowed from the current status |
| 409 | `conflict` | The resource is not in a state that allows this, like deleting a campaign people were in |
| 422 | `invalid_reference` | An id in the body does not exist in this project, like a `template_id` |
| 422 | `campaign_incomplete` | A campaign or broadcast has no step or no content to send |
| 422 | `sender_not_ready` | No sender, a failing sender, or the From domain is not verified |
| 422 | `postal_address_missing` | Marketing email needs the project’s postal address |
| 422 | `recipient_blocked` | The address is blocked in this project |
| 429 | `rate_limited` | Too many requests; see `retry-after` |
| 500 | `server_error` | Something failed on our side; it is reported to us. Try again |
| 503 | `webhook_not_configured` | A provider called a webhook that is not set up yet |

## Retrying

- `429`: wait the number of seconds in the `retry-after` header.
- `500`: retry with a growing wait. For transactional email, send an `idempotency_key` so a retry can never send twice.
- `4xx` other than `429`: fix the request first; the same request fails the same way.
