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:
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:
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). 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). 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).
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).
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).
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).
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). Or answer failures with the HttpError itself.