nautilus_event_store/error.rs
1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3// https://nautechsystems.io
4//
5// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6// You may not use this file except in compliance with the License.
7// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Error types for the event store.
17
18use thiserror::Error;
19
20/// Errors returned by event store backends, the writer, the reader, and the verifier.
21///
22/// The variants form a typed surface for the operational policies described in the SPEC: disk
23/// pressure halts the kernel, hash mismatch quarantines the affected run, and a crashed
24/// predecessor is sealed by the caller before a new run is opened.
25#[derive(Debug, Error)]
26pub enum EventStoreError {
27 /// A backend operation failed for a reason that does not fit any other variant.
28 ///
29 /// Typically wraps a redb error or an in-memory backend invariant violation.
30 #[error("backend error: {0}")]
31 Backend(String),
32 /// A run file is structurally damaged and cannot be opened safely.
33 ///
34 /// Surfaces redb header-region corruption and any backend-detected structural failure.
35 #[error("corrupted run: {0}")]
36 Corrupted(String),
37 /// The backing storage refused the write because of disk pressure.
38 ///
39 /// Maps to redb `Io(FileTooLarge)` and equivalent host-level failures (ENOSPC,
40 /// `RLIMIT_FSIZE`). Triggers the kernel halt path; the writer fail-stops.
41 #[error("disk error: {0}")]
42 Disk(String),
43 /// The canonical entry hash recorded with a row does not match the recomputed hash.
44 ///
45 /// Quarantines the affected run.
46 #[error("entry hash mismatch at seq {seq}")]
47 HashMismatch {
48 /// The sequence number whose stored hash did not match.
49 seq: u64,
50 },
51 /// The writer received an entry whose sequence number is not contiguous after the
52 /// current durable high-watermark.
53 #[error(
54 "out-of-order seq: received {seq}, expected contiguous after high-watermark {high_watermark}"
55 )]
56 OutOfOrder {
57 /// The current durable high-watermark.
58 high_watermark: u64,
59 /// The offending sequence number.
60 seq: u64,
61 },
62 /// The run is sealed and cannot accept further writes.
63 #[error("run is closed")]
64 Closed,
65 /// A reader processing `seq=N+1` did not observe `seq=N`.
66 ///
67 /// One of the four idempotency primitives: gap detection on read.
68 #[error("gap detected: missing seq {missing} between {prev} and {next}")]
69 Gap {
70 /// The last seq the reader observed.
71 prev: u64,
72 /// The next seq the reader observed.
73 next: u64,
74 /// The first missing seq.
75 missing: u64,
76 },
77 /// Opening a run found a predecessor whose status is `Running` and that lacks a
78 /// `RunEnded` entry.
79 ///
80 /// The kernel is expected to seal the predecessor (as `CrashedRecovered`, or
81 /// `Quarantined` if hash check fails) and then open a new run that records the
82 /// predecessor's `run_id` as `parent_run_id`.
83 #[error("crashed predecessor run requires sealing")]
84 CrashedPredecessor,
85}