# Handling Errors

Apart from a write whose outcome is unknown, a view that has not caught up with a write, and a few refusals of values that usually come from a caller, which belong to no category (see below), every failure of ArchitectureKit itself in reading and writing belongs to one of three categories.

Every error of the kit implements the `Categorized` trait, whose `category` method returns the category as a `Category`, or `None` for a failure that belongs to none. **Ask for the category rather than for a concrete failure**, so that new failures do not break existing code:

- `Category::Domain` means that a business rule refused the command, as with `DomainError`.
- `Category::Transient` means that trying again may help, for example if the database can not be reached. A write that fails with it has certainly stored nothing, so trying it again can not store its events twice. **A precondition that did not hold is a transient failure of its own variant, `Transient::Conflict`**, since the same command may succeed once it is decided on the state that has moved on.
- `Category::Permanent` means that trying again will not help, for example if an event could not be decoded, if the data of an event can not be encoded as JSON, if an event does not match the schema of its type, if a subject contains an event type the state has no rule for, if a command declares no precondition, or if the database does not support what a request needs, and has to be upgraded.

Every operation has an error of its own with exactly the cases it can end in, such as `ExecuteError` for `execute`, `LoadError` for `load`, and `WriteError` for `write`, so that a `match` without a catch-all arm handles all of them. Every one of them turns into `architecturekit::Error`, so that a function that calls several operations can pass their failures on with `?`.

A failure of the database is sorted by what its answer means, the same way for reading and for writing, apart from a write whose outcome is unknown (see below). The database names each error it reports with a code, such as `https://docs.eventsourcingdb.io/errors/precondition-violated`, and **it is the code that decides**:

- If a precondition did not hold (`precondition-violated`), the failure is a `Transient::Conflict`.
- If the database refuses the request for now, since it is shutting down (`server-shutting-down`), its store is read-only (`store-read-only`), it rebuilds its indexes (`indexes-rebuilding`), it restores a backup (`restore-in-progress`), its disk is full (`disk-full`), its store has failed and waits for a restart (`store-failed`), or its license has run out (`license-invalid`) or its store has reached the capacity of the license (`capacity-exceeded`), the failure belongs to `Category::Transient`. Trying again only helps once somebody has dealt with the cause in some of these cases, but reading the state anew never does, so none of them is a conflict.
- Every other code means that the request itself is wrong, for example a malformed subject, an event that does not match the schema of its type, or a range of `LowerBound::FromLatestEvent` that no event can satisfy, since the latest event of the type comes after the upper bound, and the failure belongs to `Category::Permanent` (see **[Reading Events](/docs/architecturekit/rust/reading-events)**). So does a code that the kit does not know, from a newer database.

A failure without a code is sorted by what happened:

- If the database can not be reached, if the connection breaks or stalls, if the request did not arrive in time (`408`), if the database asks to slow down (`429`), or if it is unable to answer for now (`5xx`), the failure belongs to `Category::Transient`. So does an error within a stream that a database that only offers the v1 API reports without a code, which it only does when reading is disturbed, for example because it is shutting down.
- If the answer does not come from an EventSourcingDB, the failure belongs to `Category::Transient` as well, since a proxy in front of the database answers on its own while the database restarts. The message says so, so that a wrong address stands out in the log.
- If the client does not send the request at all, since the database does not support what it needs, such as a recursive precondition, which a database that only offers the v1 API can not check, the failure belongs to `Category::Permanent`, since only upgrading the database helps. The message says so. A write that fails so has certainly stored nothing, since it was never sent.
- If the database rejects the API token (`401`), or if it rejects the request itself, for example because it is too large (`413`), the failure belongs to `Category::Permanent`. For a rejected API token, the message says so.

*Note that a database that only offers the v1 API, such as EventSourcingDB 1.2, reports its errors without a code. The client gives those that clearly match an error of the v2 API its code, and the kit sorts the others by their status. A `409` without a code is a conflict when writing, unless the database says that an event does not match its schema, which is permanent, and when reading, which knows no preconditions, it is a refusal like any other, and permanent.*

```rust
match store.execute(&borrow_book(), command).await {
  Ok(written) => {
    // ...
  }
  Err(ExecuteError::Decider(refusal)) => {
    // A business rule refused the command.
  }
  Err(ExecuteError::Transient(Transient::Conflict(_))) => {
    // A precondition did not hold.
  }
  Err(ExecuteError::Transient(_)) => {
    // Trying again may help.
  }
  Err(ExecuteError::Permanent(_)) => {
    // Trying again will not help.
  }
  Err(ExecuteError::OutcomeUnknown(_)) => {
    // The events may have been stored, so find out before trying again.
  }
}
```

*Note that `Transient` may gain variants, so a `match` on it needs a catch-all arm, and the arm for `Transient::Conflict` comes before it. `ExecuteError::Decider` holds the error of the decider, whose category is the one that error names.*

A failure of the database keeps the error of the client as its source, after its category. So `source`, of the `Error` trait of the standard library, and a `downcast_ref` to `eventsourcingdb::error::ClientError` reach the `ClientError::DbApiError` with the status, the code, the description, and the details the database gave, for example to log them, as does the `cause` method of the `Failure` that the variants of `Transient` and `Permanent` hold. **It is the category, though, that tells what the failure means**, so check for it first. For a conflict, a database that offers the v2 API names the precondition that did not hold in the details, by its index among the preconditions of the command or the write, as in `{"precondition":1}`, while a database that offers just the v1 API does not (see **[Guarding Against Concurrent Changes](/docs/architecturekit/rust/using-preconditions#guarding-against-concurrent-changes)**).

**An error that your own code returns keeps its type**, and names its own category, which is why its type implements `Categorized`: `execute` hands the error of a decider over as `ExecuteError::Decider`, and a run of a projection hands the error of the projection over as `ProjectionError::Projection`, together with its category. The text of the latter names the event the projection failed at, as in `applying event 42: …`, and the error itself can be downcast to its type. A panic in a projection, on the other hand, comes back as `ProjectionError::Panicked`, which belongs to `Category::Permanent` (see **[Running Projections](/docs/architecturekit/rust/projections#running-projections)**). The `httpapi` module has an error of its own, `HttpError`, which every error of the kit that a handler meets turns into with `?`, apart from `NotCaughtUp` (see below), and which answers it with a status code (see **[Mapping Errors to Status Codes](/docs/architecturekit/rust/mapping-errors-to-status-codes)** and **[Getting a Single Item](/docs/architecturekit/rust/defining-queries#getting-a-single-item)**).

If the future of a read or a write is dropped, for example by `tokio::time::timeout`, reading stops, and so does writing, but only before it has begun. **This is never a partial success**: a read that is dropped returns nothing, rather than part of a state, and dropping the future of `execute` writes nothing as long as the write has not begun, also while the decider decides. A write that has begun, though, runs on a task of its own until it is finished, since the database may store the events as soon as the request has left, and if it fails after its caller has stopped waiting, the failure is logged with the `log` crate. What limits the wait for its answer is the stall timeout of the client, which ends the request once the database stops answering (see **[Configuring the Stall Timeout](/docs/eventsourcingdb/sdks/rust#configuring-the-stall-timeout)**).

One exception is `ExecuteError::OutcomeUnknown`, and `WriteError::OutcomeUnknown` for `write`, which belongs to no category. It means that **a write failed in a way that leaves open whether the database stored the events**, which the client decides and reports with `ClientError::OutcomeUnknown`. The client had connected, but no answer arrived that says that nothing was stored, for example because the connection broke, the answer was cut off, or the database stalled. Or the answer was one that may come after the events were stored, such as `500`, or one that does not come from an EventSourcingDB, such as a `502` or `504` of a proxy in front of it. Its text names the write, as in `outcome unknown: writing "/books/42"`, and its source is the error the write failed with, such as a connection that was reset. `HttpError` answers it with `500 Internal Server Error` (see **[Mapping Errors to Status Codes](/docs/architecturekit/rust/mapping-errors-to-status-codes)**).

It is not transient, since trying again may store the events twice: with `Precondition::unconditionally`, the same events are written once more, and with `Precondition::on_state_read`, `execute` reads the state anew, which then holds the events, so the precondition holds, and the decider decides once more. So do not try again blindly. Find out first whether the events were stored, for example by reading the subject, or try again only with a precondition that refuses the same events a second time, such as `on_event_id` with the revision the command was decided on, or `on_pristine_subject` for a subject that the command creates. Neither `execute` nor `write` tries again by itself, not even on a store that decides again on conflicts.

Only a write that certainly stored nothing keeps its category: one for which the client could not connect to the database at all, one that the client did not send, since the database does not support what it needs, and one that the database refused, with a code, such as `precondition-violated`, or with a status that it only answers before writing anything. That way, **a transient failure means for a write as well that trying it again unchanged is safe**. The client applies the same rule in every SDK of EventSourcingDB, which errs on the safe side: a request that was cut off after connecting, but before all of it was sent, leaves the outcome open as well. Reading writes nothing, so it is not affected: there, every failure of the database belongs to a category, as described above.

Another exception is `NotCaughtUp`, which belongs to no category either. It means that a view did not catch up with written events, which is what `wait_for_written` fails with, whether the time it waits has run out (`NotCaughtUp::TimedOut`) or the view could not be asked (`NotCaughtUp::View`, which keeps the error of the view, and with it its category; see **[Waiting for Written Events](/docs/architecturekit/rust/reading-your-own-writes#waiting-for-written-events)**). The events were written, so the write has succeeded, and only the view lags behind. So it is not transient, since trying the write again would store its events twice, and not permanent, since the view may still catch up. Its text says why, as in `not caught up: the events were written, but the view did not catch up within 5s`. It deliberately does not turn into an `HttpError` with `?`: a handler that waits after a command answers with success, since the write has succeeded (see **[Reading Your Own Writes over HTTP](/docs/architecturekit/rust/reading-your-own-writes-over-http)**).

One more exception is `NotARevision`, which belongs to no category either. It means that a value that was meant to be a revision is not one, such as a text that `parse` refuses, or a number beyond 2^63-1 (see **[Comparing Revisions](/docs/architecturekit/rust/reading-your-own-writes#comparing-revisions)**). Its text names the value, as in `not a revision: "4 2"`. Such a value usually comes from a request, so `HttpError` answers it with `400 Bad Request`. Everything else that takes a revision, such as a bound of `read`, `Precondition::on_event_id`, or `wait_for`, takes a `Revision`, which is always one.

The same holds for a text that can not be read as a subject, `NotASubject`, and for values that can not compose one, `SubjectValuesError` (see **[Composing Subjects](/docs/architecturekit/rust/composing-subjects)**), which `HttpError` answers with `400 Bad Request` as well, and for a query that expected a single item and found none or several (see **[Getting a Single Item](/docs/architecturekit/rust/defining-queries#getting-a-single-item)**).

The last exception is `ReadError::EmptyRange`, which belongs to no category either. It means that the bounds of `read` leave no room for any event, such as `UpperBound::BeforeEvent` with the revision `0` (see **[Reading Events](/docs/architecturekit/rust/reading-events)**). Its text names the bounds, as in `empty range: no event can lie before "0"`. Such bounds usually come from a request, so `HttpError` answers it with `400 Bad Request` as well.

*Note that `Permanent::Unverified` is a permanent failure of its own variant, which means that an event failed its verification (see **[Verifying Events](/docs/architecturekit/rust/verifying-events)**). Since that may point to a security incident rather than a mistake, match it before the other permanent failures if you want to treat it differently, for example to raise an alarm. `Permanent` may gain variants as well, so a `match` on it needs a catch-all arm.*

*Note that `execute` does not try again by itself, unless the store decides again on conflicts (see **[Guarding Against Concurrent Changes](/docs/architecturekit/rust/using-preconditions#guarding-against-concurrent-changes)**). To try again after a transient failure, call `execute` again, which is safe, since a write that failed with a transient failure has stored nothing. After an unknown outcome, find out first whether the events were stored (see above).*
