# Errors

Every error is an RFC 9457 problem details body, with the media type
`application/problem+json`:

```json
{
  "type": "https://oscaraccountant.com/errors/period_locked",
  "title": "The period is locked",
  "status": 409,
  "code": "period_locked",
  "detail": "Period 2026-03 is locked. Reopen it or post in an open period."
}
```

| Field    | Meaning                                                       |
| -------- | ------------------------------------------------------------- |
| `code`   | **Stable and machine-readable.** Branch on it in your code    |
| `status` | The HTTP status, the same as the status of the response       |
| `title`  | A short text for a person. It can change                      |
| `detail` | What went wrong in this request, for a person. It can change  |
| `type`   | A URL that names the problem: the `code` after `/errors/`     |
| `errors` | Only for a validation error: one entry for each invalid field |

A validation error names each field:

```json
{
  "type": "https://oscaraccountant.com/errors/validation_error",
  "title": "The request is not valid",
  "status": 400,
  "code": "validation_error",
  "detail": "The request has invalid fields.",
  "errors": [
    { "field": "lines.0.amount", "code": "required", "detail": "This field is required." },
    {
      "field": "cursor",
      "code": "invalid_cursor",
      "detail": "The cursor is not a cursor of this list. Start again from the first page."
    }
  ]
}
```

`field` is a dotted path into the request (`lines.0.amount`), or `null` for
an error of the whole request.

## Status codes

| Status   | When                                                                                                                        | Retry?                               |
| -------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| 400      | The request is malformed or not valid                                                                                       | No. Fix the request                  |
| 401      | No credential, or a credential that is not valid (`not_authenticated`, `authentication_failed`)                             | No. Fix the credential               |
| 403      | The caller may not do this (`permission_denied`, `insufficient_scope`, `human_actor_required`)                              | No                                   |
| 404      | The object does not exist, or the caller cannot see it (`not_found`)                                                        | No                                   |
| 409      | The books are in a state that refuses the change                                                                            | Not as it is. Read the `code`        |
| 413, 415 | A body or a file is too large (`request_too_large`, `file_too_large`) or of a type we do not take (`unsupported_file_type`) | No                                   |
| 429      | Too many requests (`throttled`)                                                                                             | Yes, after `Retry-After`             |
| 500      | An error on our side. The `detail` never repeats internal text                                                              | Yes, with the same `Idempotency-Key` |
| 503      | The books of the company are not available now (`books_unavailable`)                                                        | Yes, later                           |

## Common codes

| Code                      | Status | Meaning                                                                                     |
| ------------------------- | ------ | ------------------------------------------------------------------------------------------- |
| `validation_error`        | 400    | See `errors` for each field                                                                 |
| `invalid_payload`         | 400    | The books refused the content of the request                                                |
| `unbalanced_entry`        | 400    | The amounts do not balance                                                                  |
| `not_authenticated`       | 401    | No credential                                                                               |
| `authentication_failed`   | 401    | An unknown, revoked or expired API key or token                                             |
| `permission_denied`       | 403    | The role, or the client capabilities of the company, do not allow it                        |
| `insufficient_scope`      | 403    | The role allows it, the scopes of the credential do not                                     |
| `human_actor_required`    | 403    | A person must make this decision. See [Human judgements](/docs/api/guides/human-judgements) |
| `not_found`               | 404    | No such object, or not visible to the caller                                                |
| `invalid_state`           | 409    | The object is not in a state that allows this, for example issuing an invoice twice         |
| `period_locked`           | 409    | The date is in a closed period. Your accountant decides                                     |
| `no_open_period`          | 409    | No open period covers the date                                                              |
| `company_not_ready`       | 409    | The books of the company are still being set up                                             |
| `idempotency_conflict`    | 409    | The `Idempotency-Key` was used with another request body                                    |
| `idempotency_in_progress` | 409    | The first request with this `Idempotency-Key` is still running                              |
| `request_too_large`       | 413    | A JSON body is larger than 1 MB                                                             |
| `throttled`               | 429    | Too many requests                                                                           |
| `books_unavailable`       | 503    | The books cannot be read now                                                                |

The [API reference](/docs/api) lists the codes of each operation. A new code
can appear at any time. Handle an unknown code by its status.
