mkit_server/store/error.rs
1//! Storage errors, shared by the key-value and blob contracts.
2
3use std::borrow::Cow;
4
5/// A boxed backend error: `Send + Sync` on native targets only, because
6/// Workers errors are `!Send`.
7#[cfg(not(target_arch = "wasm32"))]
8pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
9/// A boxed backend error: `Send + Sync` on native targets only, because
10/// Workers errors are `!Send`.
11#[cfg(target_arch = "wasm32")]
12pub type BoxError = Box<dyn std::error::Error>;
13
14/// Why a storage call failed. A failed precondition is not an error: it is
15/// [`crate::BatchOutcome::PreconditionFailed`].
16///
17/// The pipeline maps these to a [`crate::ServerError`]; storage text never
18/// reaches a client.
19#[derive(Debug, thiserror::Error)]
20#[non_exhaustive]
21pub enum StoreError {
22 /// The request breaks the contract: an oversize key, value or batch, a
23 /// scan `limit` of 0, a malformed byte range, or a blob whose bytes do
24 /// not match its key or declared length. Nothing was written.
25 #[error("invalid storage request: {0}")]
26 Invalid(Cow<'static, str>),
27 /// A staged part's subtree hash differs from its authenticated commitment.
28 #[error("part subtree hash does not match its commitment")]
29 PartSubtreeMismatch,
30 /// A byte range starts at or past the end of a blob of `len` bytes
31 /// (HTTP 416 on the serving path).
32 #[error("byte range not satisfiable for a {len}-byte blob")]
33 RangeNotSatisfiable {
34 /// The blob's length.
35 len: u64,
36 },
37 /// The store's [`crate::StoreCapabilities`] exclude the request, e.g. a
38 /// non-ref key on a `RefsOnly` store or a multi-write batch on a store
39 /// without `atomic_multi_key`. Nothing was written.
40 #[error("unsupported by this store: {0}")]
41 Unsupported(Cow<'static, str>),
42 /// The multipart storage session was aborted, completed or reclaimed.
43 #[error("multipart upload session no longer exists")]
44 SessionGone,
45 /// A stored value failed to decode (unknown codec version, wrong length).
46 #[error("corrupt stored value: {0}")]
47 Corrupt(Cow<'static, str>),
48 /// The partition is at its storage cap and rejects writes that add
49 /// data. Reads and delete-only batches keep working, so pruning still
50 /// runs (Durable Objects and `SQLite` report `SQLITE_FULL`; see
51 /// <https://developers.cloudflare.com/durable-objects/platform/limits/>).
52 /// The pipeline answers a retryable `unavailable` ("storage partition
53 /// full"), never `resource_exhausted`.
54 #[error("storage partition full")]
55 Full,
56 /// The backend failed or could not be reached. The outcome of an
57 /// `apply` that fails this way is unknown: the pipeline re-reads.
58 #[error("storage unavailable: {0}")]
59 Unavailable(#[source] BoxError),
60}
61
62impl StoreError {
63 /// [`StoreError::Unavailable`] wrapping `source`.
64 #[must_use]
65 pub fn unavailable(source: impl Into<BoxError>) -> Self {
66 Self::Unavailable(source.into())
67 }
68}