Skip to main content

rvf_types/
error.rs

1//! Error codes and error types for the RVF format.
2//!
3//! Error codes are 16-bit unsigned integers where the high byte identifies
4//! the category and the low byte the specific error.
5
6/// Wire-format error code (u16). The high byte is the category, the low byte is
7/// the specific error within that category.
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[repr(u16)]
11pub enum ErrorCode {
12    // ---- Category 0x00: Success ----
13    /// Operation succeeded.
14    Ok = 0x0000,
15    /// Partial success (some items failed).
16    OkPartial = 0x0001,
17
18    // ---- Category 0x01: Format Errors ----
19    /// Segment magic mismatch (expected 0x52564653).
20    InvalidMagic = 0x0100,
21    /// Unsupported segment version.
22    InvalidVersion = 0x0101,
23    /// Segment hash verification failed.
24    InvalidChecksum = 0x0102,
25    /// Cryptographic signature invalid.
26    InvalidSignature = 0x0103,
27    /// Segment payload shorter than declared length.
28    TruncatedSegment = 0x0104,
29    /// Root manifest validation failed.
30    InvalidManifest = 0x0105,
31    /// No valid MANIFEST_SEG in file.
32    ManifestNotFound = 0x0106,
33    /// Segment type not recognized (advisory, not fatal).
34    UnknownSegmentType = 0x0107,
35    /// Data not at expected 64-byte boundary.
36    AlignmentError = 0x0108,
37
38    // ---- Category 0x02: Query Errors ----
39    /// Query vector dimension != index dimension.
40    DimensionMismatch = 0x0200,
41    /// No index segments available.
42    EmptyIndex = 0x0201,
43    /// Requested distance metric not available.
44    MetricUnsupported = 0x0202,
45    /// Invalid filter expression.
46    FilterParseError = 0x0203,
47    /// Requested K exceeds available vectors.
48    KTooLarge = 0x0204,
49    /// Query exceeded time budget.
50    Timeout = 0x0205,
51
52    // ---- Category 0x03: Write Errors ----
53    /// Another writer holds the lock.
54    LockHeld = 0x0300,
55    /// Lock file exists but owner process is dead.
56    LockStale = 0x0301,
57    /// Insufficient space for write.
58    DiskFull = 0x0302,
59    /// Durable write (fsync) failed.
60    FsyncFailed = 0x0303,
61    /// Segment exceeds 4 GB limit.
62    SegmentTooLarge = 0x0304,
63    /// File opened in read-only mode.
64    ReadOnly = 0x0305,
65
66    // ---- Category 0x04: Tile Errors (WASM Microkernel) ----
67    /// WASM trap (OOB, unreachable, stack overflow).
68    TileTrap = 0x0400,
69    /// Tile exceeded scratch memory (64 KB).
70    TileOom = 0x0401,
71    /// Tile computation exceeded time budget.
72    TileTimeout = 0x0402,
73    /// Malformed hub-tile message.
74    TileInvalidMsg = 0x0403,
75    /// Operation not available on this profile.
76    TileUnsupportedOp = 0x0404,
77
78    // ---- Category 0x05: Crypto Errors ----
79    /// Referenced key_id not in CRYPTO_SEG.
80    KeyNotFound = 0x0500,
81    /// Key past valid_until timestamp.
82    KeyExpired = 0x0501,
83    /// Decryption or auth tag verification failed.
84    DecryptFailed = 0x0502,
85    /// Cryptographic algorithm not implemented.
86    AlgoUnsupported = 0x0503,
87    /// Attestation quote verification failed.
88    AttestationInvalid = 0x0504,
89    /// TEE platform not supported.
90    PlatformUnsupported = 0x0505,
91    /// Attestation quote expired or nonce mismatch.
92    AttestationExpired = 0x0506,
93    /// Key is not bound to the current TEE measurement.
94    KeyNotBound = 0x0507,
95
96    // ---- Category 0x06: Lineage Errors ----
97    /// Referenced parent file not found.
98    ParentNotFound = 0x0600,
99    /// Parent file hash does not match recorded parent_hash.
100    ParentHashMismatch = 0x0601,
101    /// Lineage chain is broken (missing link).
102    LineageBroken = 0x0602,
103    /// Lineage chain contains a cycle.
104    LineageCyclic = 0x0603,
105
106    // ---- Category 0x07: COW Errors ----
107    /// COW cluster map is corrupt or unreadable.
108    CowMapCorrupt = 0x0700,
109    /// Referenced cluster not found in COW map.
110    ClusterNotFound = 0x0701,
111    /// Parent chain is broken (missing ancestor).
112    ParentChainBroken = 0x0702,
113    /// Delta patch exceeds compaction threshold.
114    DeltaThresholdExceeded = 0x0703,
115    /// Snapshot is frozen and cannot be modified.
116    SnapshotFrozen = 0x0704,
117    /// Membership filter is invalid or corrupt.
118    MembershipInvalid = 0x0705,
119    /// Generation counter is stale (concurrent modification).
120    GenerationStale = 0x0706,
121    /// Kernel binding hash does not match manifest.
122    KernelBindingMismatch = 0x0707,
123    /// Double-root manifest is corrupt.
124    DoubleRootCorrupt = 0x0708,
125}
126
127impl ErrorCode {
128    /// Return the error category (high byte).
129    #[inline]
130    pub const fn category(self) -> u8 {
131        (self as u16 >> 8) as u8
132    }
133
134    /// Return true if this code indicates success (category 0x00).
135    #[inline]
136    pub const fn is_success(self) -> bool {
137        self.category() == 0x00
138    }
139
140    /// Return true if this is a format error (category 0x01), which is generally fatal.
141    #[inline]
142    pub const fn is_format_error(self) -> bool {
143        self.category() == 0x01
144    }
145}
146
147impl TryFrom<u16> for ErrorCode {
148    type Error = u16;
149
150    fn try_from(value: u16) -> Result<Self, Self::Error> {
151        match value {
152            0x0000 => Ok(Self::Ok),
153            0x0001 => Ok(Self::OkPartial),
154
155            0x0100 => Ok(Self::InvalidMagic),
156            0x0101 => Ok(Self::InvalidVersion),
157            0x0102 => Ok(Self::InvalidChecksum),
158            0x0103 => Ok(Self::InvalidSignature),
159            0x0104 => Ok(Self::TruncatedSegment),
160            0x0105 => Ok(Self::InvalidManifest),
161            0x0106 => Ok(Self::ManifestNotFound),
162            0x0107 => Ok(Self::UnknownSegmentType),
163            0x0108 => Ok(Self::AlignmentError),
164
165            0x0200 => Ok(Self::DimensionMismatch),
166            0x0201 => Ok(Self::EmptyIndex),
167            0x0202 => Ok(Self::MetricUnsupported),
168            0x0203 => Ok(Self::FilterParseError),
169            0x0204 => Ok(Self::KTooLarge),
170            0x0205 => Ok(Self::Timeout),
171
172            0x0300 => Ok(Self::LockHeld),
173            0x0301 => Ok(Self::LockStale),
174            0x0302 => Ok(Self::DiskFull),
175            0x0303 => Ok(Self::FsyncFailed),
176            0x0304 => Ok(Self::SegmentTooLarge),
177            0x0305 => Ok(Self::ReadOnly),
178
179            0x0400 => Ok(Self::TileTrap),
180            0x0401 => Ok(Self::TileOom),
181            0x0402 => Ok(Self::TileTimeout),
182            0x0403 => Ok(Self::TileInvalidMsg),
183            0x0404 => Ok(Self::TileUnsupportedOp),
184
185            0x0500 => Ok(Self::KeyNotFound),
186            0x0501 => Ok(Self::KeyExpired),
187            0x0502 => Ok(Self::DecryptFailed),
188            0x0503 => Ok(Self::AlgoUnsupported),
189            0x0504 => Ok(Self::AttestationInvalid),
190            0x0505 => Ok(Self::PlatformUnsupported),
191            0x0506 => Ok(Self::AttestationExpired),
192            0x0507 => Ok(Self::KeyNotBound),
193
194            0x0600 => Ok(Self::ParentNotFound),
195            0x0601 => Ok(Self::ParentHashMismatch),
196            0x0602 => Ok(Self::LineageBroken),
197            0x0603 => Ok(Self::LineageCyclic),
198
199            0x0700 => Ok(Self::CowMapCorrupt),
200            0x0701 => Ok(Self::ClusterNotFound),
201            0x0702 => Ok(Self::ParentChainBroken),
202            0x0703 => Ok(Self::DeltaThresholdExceeded),
203            0x0704 => Ok(Self::SnapshotFrozen),
204            0x0705 => Ok(Self::MembershipInvalid),
205            0x0706 => Ok(Self::GenerationStale),
206            0x0707 => Ok(Self::KernelBindingMismatch),
207            0x0708 => Ok(Self::DoubleRootCorrupt),
208
209            other => Err(other),
210        }
211    }
212}
213
214/// Rust-idiomatic error type wrapping format-level failures.
215#[derive(Clone, Debug, PartialEq, Eq)]
216#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
217pub enum RvfError {
218    /// A wire-level error code was returned.
219    Code(ErrorCode),
220    /// The raw u16 did not map to a known error code.
221    UnknownCode(u16),
222    /// A segment header had an invalid magic number.
223    BadMagic { expected: u32, got: u32 },
224    /// A struct size assertion failed.
225    SizeMismatch { expected: usize, got: usize },
226    /// A value was outside the valid enum range.
227    InvalidEnumValue {
228        type_name: &'static str,
229        value: u64,
230    },
231}
232
233impl core::fmt::Display for RvfError {
234    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
235        match self {
236            Self::Code(c) => write!(f, "RVF error code 0x{:04X}", *c as u16),
237            Self::UnknownCode(v) => write!(f, "unknown RVF error code 0x{v:04X}"),
238            Self::BadMagic { expected, got } => {
239                write!(f, "bad magic: expected 0x{expected:08X}, got 0x{got:08X}")
240            }
241            Self::SizeMismatch { expected, got } => {
242                write!(f, "size mismatch: expected {expected}, got {got}")
243            }
244            Self::InvalidEnumValue { type_name, value } => {
245                write!(f, "invalid {type_name} value: {value}")
246            }
247        }
248    }
249}
250
251#[cfg(test)]
252mod tests {
253    use super::*;
254    use alloc::format;
255
256    #[test]
257    fn error_code_round_trip_all() {
258        let codes: &[(u16, ErrorCode)] = &[
259            (0x0000, ErrorCode::Ok),
260            (0x0001, ErrorCode::OkPartial),
261            (0x0100, ErrorCode::InvalidMagic),
262            (0x0101, ErrorCode::InvalidVersion),
263            (0x0102, ErrorCode::InvalidChecksum),
264            (0x0103, ErrorCode::InvalidSignature),
265            (0x0104, ErrorCode::TruncatedSegment),
266            (0x0105, ErrorCode::InvalidManifest),
267            (0x0106, ErrorCode::ManifestNotFound),
268            (0x0107, ErrorCode::UnknownSegmentType),
269            (0x0108, ErrorCode::AlignmentError),
270            (0x0200, ErrorCode::DimensionMismatch),
271            (0x0201, ErrorCode::EmptyIndex),
272            (0x0202, ErrorCode::MetricUnsupported),
273            (0x0203, ErrorCode::FilterParseError),
274            (0x0204, ErrorCode::KTooLarge),
275            (0x0205, ErrorCode::Timeout),
276            (0x0300, ErrorCode::LockHeld),
277            (0x0301, ErrorCode::LockStale),
278            (0x0302, ErrorCode::DiskFull),
279            (0x0303, ErrorCode::FsyncFailed),
280            (0x0304, ErrorCode::SegmentTooLarge),
281            (0x0305, ErrorCode::ReadOnly),
282            (0x0400, ErrorCode::TileTrap),
283            (0x0401, ErrorCode::TileOom),
284            (0x0402, ErrorCode::TileTimeout),
285            (0x0403, ErrorCode::TileInvalidMsg),
286            (0x0404, ErrorCode::TileUnsupportedOp),
287            (0x0500, ErrorCode::KeyNotFound),
288            (0x0501, ErrorCode::KeyExpired),
289            (0x0502, ErrorCode::DecryptFailed),
290            (0x0503, ErrorCode::AlgoUnsupported),
291            (0x0504, ErrorCode::AttestationInvalid),
292            (0x0505, ErrorCode::PlatformUnsupported),
293            (0x0506, ErrorCode::AttestationExpired),
294            (0x0507, ErrorCode::KeyNotBound),
295            (0x0600, ErrorCode::ParentNotFound),
296            (0x0601, ErrorCode::ParentHashMismatch),
297            (0x0602, ErrorCode::LineageBroken),
298            (0x0603, ErrorCode::LineageCyclic),
299            (0x0700, ErrorCode::CowMapCorrupt),
300            (0x0701, ErrorCode::ClusterNotFound),
301            (0x0702, ErrorCode::ParentChainBroken),
302            (0x0703, ErrorCode::DeltaThresholdExceeded),
303            (0x0704, ErrorCode::SnapshotFrozen),
304            (0x0705, ErrorCode::MembershipInvalid),
305            (0x0706, ErrorCode::GenerationStale),
306            (0x0707, ErrorCode::KernelBindingMismatch),
307            (0x0708, ErrorCode::DoubleRootCorrupt),
308        ];
309        for &(raw, expected) in codes {
310            assert_eq!(ErrorCode::try_from(raw), Ok(expected), "code 0x{raw:04X}");
311            assert_eq!(expected as u16, raw);
312        }
313    }
314
315    #[test]
316    fn unknown_code() {
317        assert_eq!(ErrorCode::try_from(0x9999), Err(0x9999));
318    }
319
320    #[test]
321    fn category_extraction() {
322        assert_eq!(ErrorCode::Ok.category(), 0x00);
323        assert_eq!(ErrorCode::InvalidMagic.category(), 0x01);
324        assert_eq!(ErrorCode::DimensionMismatch.category(), 0x02);
325        assert_eq!(ErrorCode::LockHeld.category(), 0x03);
326        assert_eq!(ErrorCode::TileTrap.category(), 0x04);
327        assert_eq!(ErrorCode::KeyNotFound.category(), 0x05);
328        assert_eq!(ErrorCode::ParentNotFound.category(), 0x06);
329        assert_eq!(ErrorCode::CowMapCorrupt.category(), 0x07);
330    }
331
332    #[test]
333    fn success_check() {
334        assert!(ErrorCode::Ok.is_success());
335        assert!(ErrorCode::OkPartial.is_success());
336        assert!(!ErrorCode::InvalidMagic.is_success());
337    }
338
339    #[test]
340    fn format_error_check() {
341        assert!(ErrorCode::InvalidMagic.is_format_error());
342        assert!(!ErrorCode::Ok.is_format_error());
343        assert!(!ErrorCode::DimensionMismatch.is_format_error());
344    }
345
346    #[test]
347    fn rvf_error_display() {
348        let e = RvfError::BadMagic {
349            expected: 0x52564653,
350            got: 0x00000000,
351        };
352        let s = format!("{e}");
353        assert!(s.contains("bad magic"));
354        assert!(s.contains("52564653"));
355    }
356
357    #[test]
358    fn cow_error_check() {
359        assert_eq!(ErrorCode::CowMapCorrupt as u16, 0x0700);
360        assert_eq!(ErrorCode::ClusterNotFound as u16, 0x0701);
361        assert_eq!(ErrorCode::ParentChainBroken as u16, 0x0702);
362        assert_eq!(ErrorCode::DeltaThresholdExceeded as u16, 0x0703);
363        assert_eq!(ErrorCode::SnapshotFrozen as u16, 0x0704);
364        assert_eq!(ErrorCode::MembershipInvalid as u16, 0x0705);
365        assert_eq!(ErrorCode::GenerationStale as u16, 0x0706);
366        assert_eq!(ErrorCode::KernelBindingMismatch as u16, 0x0707);
367        assert_eq!(ErrorCode::DoubleRootCorrupt as u16, 0x0708);
368        // All COW errors should be category 0x07
369        assert_eq!(ErrorCode::CowMapCorrupt.category(), 0x07);
370        assert_eq!(ErrorCode::DoubleRootCorrupt.category(), 0x07);
371    }
372
373    #[test]
374    fn error_codes_match_spec() {
375        assert_eq!(ErrorCode::InvalidMagic as u16, 0x0100);
376        assert_eq!(ErrorCode::InvalidChecksum as u16, 0x0102);
377        assert_eq!(ErrorCode::ManifestNotFound as u16, 0x0106);
378        assert_eq!(ErrorCode::AlgoUnsupported as u16, 0x0503);
379    }
380}