# Mapping Errors to Status Codes

Every error has a matching HTTP status code, which the module `httpapi` derives from the kind of the error.

To get the status code that matches an error of the kit, turn it into an `HttpError`, which is what `?` does in a handler, and ask for its `status`:

```rust
let status = HttpError::from(error).status();
```

An error of your own, such as the one of a view in a database, turns into an `HttpError` with `HttpError::of`, by its category alone:

```rust
let status = HttpError::of(error).status();
```

An error of the kit turns into an `HttpError` by what it is, and only then by its category:

| Error | Status code |
|---|---|
| `HttpError::unauthorized` | `401 Unauthorized` |
| `HttpError::forbidden` | `403 Forbidden` |
| `HttpError::too_large` | `413 Payload Too Large` |
| `HttpError::unsupported_media_type` | `415 Unsupported Media Type` |
| `HttpError::malformed`, `NotARevision`, `NotASubject`, `SubjectValuesError`, `EmptyRange` | `400 Bad Request` |
| `HttpError::not_found`, `SingleError::NoItems` | `404 Not Found` |
| an error of the category `Category::Domain` | `422 Unprocessable Entity` |
| `ExecuteError::OutcomeUnknown`, `WriteError::OutcomeUnknown` | `500 Internal Server Error` |
| `SingleError::TooManyItems` | `500 Internal Server Error` |
| `Transient::Conflict` | `409 Conflict` |
| any other error of the category `Category::Transient` | `503 Service Unavailable` |
| an error of the category `Category::Permanent` | `500 Internal Server Error` |
| any other error | `500 Internal Server Error` |

*Note that four errors of the kit do not turn into an `HttpError` with `?`: `DomainError`, `ProjectionError`, `CaughtUpError`, and `NotCaughtUp`. A `DomainError` usually reaches a handler within the error of `execute`, which turns into one, and on its own, `HttpError::of` answers it with `422 Unprocessable Entity` and its own text, as it answers the other two by their categories. `NotCaughtUp` is left out on purpose (see below).*

*Note that `ExecuteError::OutcomeUnknown` means that a write may or may not have stored its events (see **[Handling Errors](/docs/architecturekit/rust/handling-errors)**). It belongs to no category, and is answered with `500 Internal Server Error`: trying again, which `409` and `503` invite, may store the events twice. **Its message is `outcome unknown: the request may have succeeded`**, so that the caller does not simply try again, while the error, which may name internals, such as the subject, is logged at level `error`.*

*Note that `NotCaughtUp` means that a write has succeeded, but that a view did not catch up with it (see **[Waiting for Written Events](/docs/architecturekit/rust/reading-your-own-writes#waiting-for-written-events)**). Trying again would store the events twice, and the write has succeeded, so it does not turn into an `HttpError`, which would answer a command that has succeeded as a failure. Answer it as the success it is, with `CommandAnswer::with_result`, which logs that the view lags behind (see **[Adding to the Answer](/docs/architecturekit/rust/handling-commands-over-http#adding-to-the-answer)**).*

*Note that `NotARevision` means that a value that was handed over is not a revision, such as an expected event ID that a handler reads with `parse`, which usually comes from the request. So it is answered with `400 Bad Request` and the error as the message, like any other mistake in the request. An ID that the database hands out, and that is broken, is a failure of the server, though, so the kit reports it as an error of the category `Category::Permanent`, which is answered with `500 Internal Server Error`. The same holds for `NotASubject`, and `SubjectValuesError` refuses values that can not compose a subject, such as an ID in the path of a request (see **[Composing Subjects](/docs/architecturekit/rust/composing-subjects)**).*

*Note that `EmptyRange` means that the bounds of `read` leave no room for any event, which usually come from the request as well. So it is answered the same way, with `400 Bad Request` and the error as the message, such as `empty range: no event can lie before "0"` (see **[Reading Events](/docs/architecturekit/rust/reading-events)**).*

*Note that a panic of a handler is answered with `500 Internal Server Error` as well, once the router has the layer `CatchPanicLayer` with `answer_panic` (see **[Handling Commands over HTTP](/docs/architecturekit/rust/handling-commands-over-http)**).*

*Note that the status code says nothing about what to tell the caller. If you answer in a format of your own, take the message from the `message` method of the `HttpError`, which leaves out the error for `401`, `409`, and `500` and above, since it may name internals, says `not found` for `SingleError::NoItems`, and tells the caller for an unknown outcome that the request may have succeeded (see **[Handling Commands over HTTP](/docs/architecturekit/rust/handling-commands-over-http)**). Or answer failures with the `HttpError` itself.*
