Skip to main content

nodedb_columnar/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Error types for columnar segment operations.
4
5/// Errors from columnar segment encoding, decoding, and validation.
6#[derive(Debug, thiserror::Error)]
7#[non_exhaustive]
8pub enum ColumnarError {
9    #[error("codec error: {0}")]
10    Codec(#[from] nodedb_codec::CodecError),
11
12    #[error("segment too short: expected at least {expected} bytes, got {got}")]
13    TruncatedSegment { expected: usize, got: usize },
14
15    #[error("invalid magic bytes: expected NDBS, got {0:?}")]
16    InvalidMagic([u8; 4]),
17
18    #[error(
19        "incompatible segment version: reader {reader_major}.x, segment {segment_major}.{segment_minor}"
20    )]
21    IncompatibleVersion {
22        reader_major: u8,
23        segment_major: u8,
24        segment_minor: u8,
25    },
26
27    #[error("footer CRC32C mismatch: stored {stored:#010x}, computed {computed:#010x}")]
28    FooterCrcMismatch { stored: u32, computed: u32 },
29
30    #[error("column index {index} out of range (segment has {count} columns)")]
31    ColumnOutOfRange { index: usize, count: usize },
32
33    #[error("schema mismatch: expected {expected} columns, memtable has {got}")]
34    SchemaMismatch { expected: usize, got: usize },
35
36    #[error("memtable is empty — nothing to flush")]
37    EmptyMemtable,
38
39    #[error("serialization error: {0}")]
40    Serialization(String),
41
42    #[error("value type mismatch for column '{column}': expected {expected}")]
43    TypeMismatch { column: String, expected: String },
44
45    #[error("null violation: column '{0}' is NOT NULL")]
46    NullViolation(String),
47
48    #[error("duplicate primary key")]
49    DuplicatePrimaryKey,
50
51    #[error("primary key not found")]
52    PrimaryKeyNotFound,
53
54    #[error("segment ID space exhausted: u64::MAX segments have been allocated")]
55    SegmentIdExhausted,
56
57    /// Structural corruption detected while parsing a segment header or footer.
58    ///
59    /// `offset` is the byte position from the start of the segment where the
60    /// problem was detected. `segment_id` is `None` when the parse fails before
61    /// the id can be resolved (e.g. inside the footer itself).
62    #[error("segment corruption in {segment_id:?} at offset {offset:?}: {reason}")]
63    Corruption {
64        segment_id: Option<String>,
65        reason: String,
66        offset: Option<u64>,
67    },
68
69    /// Segment is encrypted (starts with `SEGV`) but no KEK was supplied.
70    #[error(
71        "columnar segment is encrypted but no encryption key was provided; \
72         cannot read an encrypted segment without a key"
73    )]
74    MissingKek,
75
76    /// Segment is plaintext (`NDBS`) but a KEK is configured (policy violation).
77    #[error(
78        "columnar segment is plaintext but an encryption key is configured; \
79         refusing to load an unencrypted segment when encryption is required"
80    )]
81    KekRequired,
82
83    /// AES-256-GCM encryption of the segment payload failed.
84    #[error("columnar segment encryption failed: {0}")]
85    EncryptionFailed(String),
86
87    /// AES-256-GCM decryption of the segment payload failed.
88    #[error("columnar segment decryption failed: {0}")]
89    DecryptionFailed(String),
90
91    /// Memory budget exhausted or global ceiling exceeded.
92    #[error("memory budget exhausted: {0}")]
93    BudgetExhausted(#[from] nodedb_mem::MemError),
94}