# Errors

mint uses HTTP status codes: `2xx` success, `4xx` a problem with the request, `5xx` a problem on the mint side.

All errors share one format:

```json
{ "error": { "code": "forbidden_scope", "message": "…", "request_id": "req_…", "field": "…", "details": {} } }
```

`code` is stable and machine readable, `message` is for humans and may change, `field` names the affected
input. Every response carries an `X-Request-Id` header; please quote it when you contact support.

### Handling errors

- Branch on `error.code`, never on `message`.
- `429` and `503` are temporary: wait for `Retry-After` seconds and retry the same request with the same
  `Idempotency-Key`.
- `409` codes with `Retry-After` (for example `film_busy`, `idempotency_in_progress`) are temporary as well.
- New codes may be added within v1. Treat an unknown `4xx` code like `invalid_request` and an unknown `5xx` code
  like `internal`.

### Error codes

| Code | HTTP status | Described at |
|---|---|---|
| `invalid_request` | 400 | `POST /films` |
| `invalid_json` | 400 | general |
| `invalid_cursor` | 400 | `GET /events`<br>`GET /films/{id}/comments` |
| `idempotency_key_required` | 400 | `POST /films` |
| `invalid_idempotency_key` | 400 | general |
| `parent_not_found` | 400 | general |
| `max_depth_exceeded` | 400 | general |
| `narration_too_long` | 400 | general |
| `user_not_in_company` | 400 | general |
| `template_not_found` | 400 | general |
| `category_not_found` | 400 | general |
| `feedback_kind_unsupported` | 400 | general |
| `upload_not_found` | 400 | general |
| `unauthorized` | 401 | general |
| `forbidden_scope` | 403 | `GET /operations/{id}` |
| `ip_not_allowed` | 403 | `GET /operations/{id}` |
| `insufficient_credits` | 402 | 5 endpoints |
| `company_inactive` | 403 | `GET /operations/{id}` |
| `api_not_enabled` | 403 | `GET /operations/{id}` |
| `not_found` | 404 | 10 endpoints |
| `not_approved` | 404 | `GET /films/{id}/file`<br>`GET /films/{id}/subtitles.srt` |
| `subtitles_not_available` | 404 | `GET /films/{id}/subtitles.srt` |
| `method_not_allowed` | – | general |
| `category_exists` | 409 | `POST /categories` |
| `external_key_exists` | 409 | `PATCH /categories/{id}` |
| `external_key_conflict` | 409 | general |
| `category_limit_reached` | 409 | `POST /categories` |
| `idempotency_in_progress` | 409 | 5 endpoints |
| `version_conflict` | 409 | `POST /films` |
| `previous_version_creating` | 409 | `POST /films` |
| `film_deleted` | 409 | `POST /films` |
| `not_cancellable` | 409 | `POST /films/{id}/cancel` |
| `file_unavailable` | 409 | `GET /films/{id}/file`<br>`GET /films/{id}/storyboard-preview/file`<br>`GET /films/{id}/subtitles.srt` |
| `storyboard_not_ready` | 409 | `POST /films/{id}/feedback`<br>`POST /films/{id}/storyboard-preview` |
| `storyboard_not_current` | 409 | `POST /films/{id}/storyboard-preview` |
| `timeline_outdated` | 409 | `POST /films/{id}/feedback` |
| `preview_in_progress` | 409 | `POST /films/{id}/storyboard-preview` |
| `preview_not_ready` | 409 | `GET /films/{id}/storyboard-preview/file` |
| `preview_unsupported` | 409 | `POST /films/{id}/storyboard-preview` |
| `feedback_queue_full` | 409 | `POST /films/{id}/feedback` |
| `feedback_not_cancellable` | 409 | `POST /films/{id}/feedback/{feedback_id}/cancel` |
| `feedback_not_retryable` | 409 | `POST /films/{id}/feedback/{feedback_id}/retry` |
| `feedback_busy` | 409 | `POST /films/{id}/feedback/{feedback_id}/cancel`<br>`POST /films/{id}/feedback/{feedback_id}/retry` |
| `revision_locked` | 409 | `POST /films/{id}/comments` |
| `film_not_ready` | 409 | `GET /films/{id}/comments`<br>`POST /films/{id}/comments`<br>`POST /films/{id}/comments/apply` |
| `film_busy` | 409 | `POST /films/{id}/comments/apply` |
| `no_open_comments` | 409 | `POST /films/{id}/comments/apply` |
| `comments_not_open` | 409 | `POST /films/{id}/comments/apply` |
| `correction_in_progress` | 409 | `POST /films/{id}/comments/apply` |
| `upload_quota_exceeded` | 409 | `POST /uploads` |
| `film_cancelled` | 409 | 5 endpoints |
| `cursor_expired` | 410 | `GET /events` |
| `preview_expired` | 410 | `GET /films/{id}/storyboard-preview/file` |
| `upload_expired` | 410 | `POST /films/{id}/comments`<br>`POST /films/{id}/feedback`<br>`GET /uploads/{id}` |
| `length_required` | 411 | `POST /uploads` |
| `payload_too_large` | 413 | `POST /uploads` |
| `unsupported_media_type` | 415 | `POST /uploads` |
| `file_content_mismatch` | 415 | `POST /uploads` |
| `range_not_satisfiable` | 416 | `GET /films/{id}/file`<br>`GET /films/{id}/storyboard-preview/file`<br>`GET /films/{id}/subtitles.srt` |
| `idempotency_mismatch` | 422 | `POST /films/{id}/comments/apply`<br>`POST /films/{id}/feedback`<br>`POST /films/{id}/storyboard-preview` |
| `scene_not_found` | 422 | `POST /films/{id}/feedback` |
| `rate_limited` | 429 | `GET /films/{id}/file`<br>`POST /uploads` |
| `internal` | 500 | general |
| `service_unavailable` | 503 | `PATCH /categories/{id}` |

```json Error response
{
  "error": {
    "code": "version_conflict",
    "message": "Version 1.0 of this video_key already exists with other content.",
    "request_id": "req_5b2e8f1a4c7d0e3b6a9f2c5d8e1b4a7c",
    "field": "version",
    "details": { "film_id": "k7m2xq9v4t8n1b5r3c6d0wza" }
  }
}
```

```http Response headers
HTTP/1.1 409 Conflict
Content-Type: application/json
X-Request-Id: req_5b2e8f1a4c7d0e3b6a9f2c5d8e1b4a7c
```
