Skip to main content

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}