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 0x08: Security Errors (ADR-033) ----
107    /// Level 0 manifest has no signature in Strict/Paranoid mode.
108    UnsignedManifest = 0x0800,
109    /// Content hash mismatch on a hotset-referenced segment.
110    ContentHashMismatch = 0x0801,
111    /// Manifest signer is not in the trust store.
112    UnknownSigner = 0x0802,
113    /// Centroid epoch drift exceeds maximum allowed.
114    EpochDriftExceeded = 0x0803,
115    /// Level 1 manifest signature invalid (Paranoid mode).
116    Level1InvalidSignature = 0x0804,
117
118    // ---- Category 0x09: Quality Errors (ADR-033) ----
119    /// Query result quality is below threshold and AcceptDegraded not set.
120    QualityBelowThreshold = 0x0900,
121    /// Per-connection budget tokens exhausted (DoS protection).
122    BudgetTokensExhausted = 0x0901,
123    /// Query signature is blacklisted (repeated degenerate queries).
124    QueryBlacklisted = 0x0902,
125
126    // ---- Category 0x07: COW Errors ----
127    /// COW cluster map is corrupt or unreadable.
128    CowMapCorrupt = 0x0700,
129    /// Referenced cluster not found in COW map.
130    ClusterNotFound = 0x0701,
131    /// Parent chain is broken (missing ancestor).
132    ParentChainBroken = 0x0702,
133    /// Delta patch exceeds compaction threshold.
134    DeltaThresholdExceeded = 0x0703,
135    /// Snapshot is frozen and cannot be modified.
136    SnapshotFrozen = 0x0704,
137    /// Membership filter is invalid or corrupt.
138    MembershipInvalid = 0x0705,
139    /// Generation counter is stale (concurrent modification).
140    GenerationStale = 0x0706,
141    /// Kernel binding hash does not match manifest.
142    KernelBindingMismatch = 0x0707,
143    /// Double-root manifest is corrupt.
144    DoubleRootCorrupt = 0x0708,
145}
146
147impl ErrorCode {
148    /// Return the error category (high byte).
149    #[inline]
150    pub const fn category(self) -> u8 {
151        (self as u16 >> 8) as u8
152    }
153
154    /// Return true if this code indicates success (category 0x00).
155    #[inline]
156    pub const fn is_success(self) -> bool {
157        self.category() == 0x00
158    }
159
160    /// Return true if this is a format error (category 0x01), which is generally fatal.
161    #[inline]
162    pub const fn is_format_error(self) -> bool {
163        self.category() == 0x01
164    }
165
166    /// Return true if this is a security error (category 0x08).
167    #[inline]
168    pub const fn is_security_error(self) -> bool {
169        self.category() == 0x08
170    }
171
172    /// Return true if this is a quality error (category 0x09).
173    #[inline]
174    pub const fn is_quality_error(self) -> bool {
175        self.category() == 0x09
176    }
177}
178
179impl TryFrom<u16> for ErrorCode {
180    type Error = u16;
181
182    fn try_from(value: u16) -> Result<Self, Self::Error> {
183        match value {
184            0x0000 => Ok(Self::Ok),
185            0x0001 => Ok(Self::OkPartial),
186
187            0x0100 => Ok(Self::InvalidMagic),
188            0x0101 => Ok(Self::InvalidVersion),
189            0x0102 => Ok(Self::InvalidChecksum),
190            0x0103 => Ok(Self::InvalidSignature),
191            0x0104 => Ok(Self::TruncatedSegment),
192            0x0105 => Ok(Self::InvalidManifest),
193            0x0106 => Ok(Self::ManifestNotFound),
194            0x0107 => Ok(Self::UnknownSegmentType),
195            0x0108 => Ok(Self::AlignmentError),
196
197            0x0200 => Ok(Self::DimensionMismatch),
198            0x0201 => Ok(Self::EmptyIndex),
199            0x0202 => Ok(Self::MetricUnsupported),
200            0x0203 => Ok(Self::FilterParseError),
201            0x0204 => Ok(Self::KTooLarge),
202            0x0205 => Ok(Self::Timeout),
203
204            0x0300 => Ok(Self::LockHeld),
205            0x0301 => Ok(Self::LockStale),
206            0x0302 => Ok(Self::DiskFull),
207            0x0303 => Ok(Self::FsyncFailed),
208            0x0304 => Ok(Self::SegmentTooLarge),
209            0x0305 => Ok(Self::ReadOnly),
210
211            0x0400 => Ok(Self::TileTrap),
212            0x0401 => Ok(Self::TileOom),
213            0x0402 => Ok(Self::TileTimeout),
214            0x0403 => Ok(Self::TileInvalidMsg),
215            0x0404 => Ok(Self::TileUnsupportedOp),
216
217            0x0500 => Ok(Self::KeyNotFound),
218            0x0501 => Ok(Self::KeyExpired),
219            0x0502 => Ok(Self::DecryptFailed),
220            0x0503 => Ok(Self::AlgoUnsupported),
221            0x0504 => Ok(Self::AttestationInvalid),
222            0x0505 => Ok(Self::PlatformUnsupported),
223            0x0506 => Ok(Self::AttestationExpired),
224            0x0507 => Ok(Self::KeyNotBound),
225
226            0x0600 => Ok(Self::ParentNotFound),
227            0x0601 => Ok(Self::ParentHashMismatch),
228            0x0602 => Ok(Self::LineageBroken),
229            0x0603 => Ok(Self::LineageCyclic),
230
231            0x0800 => Ok(Self::UnsignedManifest),
232            0x0801 => Ok(Self::ContentHashMismatch),
233            0x0802 => Ok(Self::UnknownSigner),
234            0x0803 => Ok(Self::EpochDriftExceeded),
235            0x0804 => Ok(Self::Level1InvalidSignature),
236
237            0x0900 => Ok(Self::QualityBelowThreshold),
238            0x0901 => Ok(Self::BudgetTokensExhausted),
239            0x0902 => Ok(Self::QueryBlacklisted),
240
241            0x0700 => Ok(Self::CowMapCorrupt),
242            0x0701 => Ok(Self::ClusterNotFound),
243            0x0702 => Ok(Self::ParentChainBroken),
244            0x0703 => Ok(Self::DeltaThresholdExceeded),
245            0x0704 => Ok(Self::SnapshotFrozen),
246            0x0705 => Ok(Self::MembershipInvalid),
247            0x0706 => Ok(Self::GenerationStale),
248            0x0707 => Ok(Self::KernelBindingMismatch),
249            0x0708 => Ok(Self::DoubleRootCorrupt),
250
251            other => Err(other),
252        }
253    }
254}
255
256/// Rust-idiomatic error type wrapping format-level failures.
257#[derive(Clone, Debug, PartialEq, Eq)]
258#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
259pub enum RvfError {
260    /// A wire-level error code was returned.
261    Code(ErrorCode),
262    /// The raw u16 did not map to a known error code.
263    UnknownCode(u16),
264    /// A segment header had an invalid magic number.
265    BadMagic { expected: u32, got: u32 },
266    /// A struct size assertion failed.
267    SizeMismatch { expected: usize, got: usize },
268    /// A value was outside the valid enum range.
269    InvalidEnumValue {
270        type_name: &'static str,
271        value: u64,
272    },
273    /// Security policy violation during file open (ADR-033 §4).
274    Security(crate::security::SecurityError),
275    /// Query result quality is below threshold (ADR-033 §2.4).
276    /// Contains the QualityEnvelope with partial results and diagnostics.
277    QualityBelowThreshold {
278        quality: crate::quality::ResponseQuality,
279        reason: &'static str,
280    },
281}
282
283impl core::fmt::Display for RvfError {
284    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
285        match self {
286            Self::Code(c) => write!(f, "RVF error code 0x{:04X}", *c as u16),
287            Self::UnknownCode(v) => write!(f, "unknown RVF error code 0x{v:04X}"),
288            Self::BadMagic { expected, got } => {
289                write!(f, "bad magic: expected 0x{expected:08X}, got 0x{got:08X}")
290            }
291            Self::SizeMismatch { expected, got } => {
292                write!(f, "size mismatch: expected {expected}, got {got}")
293            }
294            Self::InvalidEnumValue { type_name, value } => {
295                write!(f, "invalid {type_name} value: {value}")
296            }
297            Self::Security(e) => write!(f, "security error: {e}"),
298            Self::QualityBelowThreshold { quality, reason } => {
299                write!(f, "quality below threshold ({quality:?}): {reason}")
300            }
301        }
302    }
303}
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308    use alloc::format;
309
310    #[test]
311    fn error_code_round_trip_all() {
312        let codes: &[(u16, ErrorCode)] = &[
313            (0x0000, ErrorCode::Ok),
314            (0x0001, ErrorCode::OkPartial),
315            (0x0100, ErrorCode::InvalidMagic),
316            (0x0101, ErrorCode::InvalidVersion),
317            (0x0102, ErrorCode::InvalidChecksum),
318            (0x0103, ErrorCode::InvalidSignature),
319            (0x0104, ErrorCode::TruncatedSegment),
320            (0x0105, ErrorCode::InvalidManifest),
321            (0x0106, ErrorCode::ManifestNotFound),
322            (0x0107, ErrorCode::UnknownSegmentType),
323            (0x0108, ErrorCode::AlignmentError),
324            (0x0200, ErrorCode::DimensionMismatch),
325            (0x0201, ErrorCode::EmptyIndex),
326            (0x0202, ErrorCode::MetricUnsupported),
327            (0x0203, ErrorCode::FilterParseError),
328            (0x0204, ErrorCode::KTooLarge),
329            (0x0205, ErrorCode::Timeout),
330            (0x0300, ErrorCode::LockHeld),
331            (0x0301, ErrorCode::LockStale),
332            (0x0302, ErrorCode::DiskFull),
333            (0x0303, ErrorCode::FsyncFailed),
334            (0x0304, ErrorCode::SegmentTooLarge),
335            (0x0305, ErrorCode::ReadOnly),
336            (0x0400, ErrorCode::TileTrap),
337            (0x0401, ErrorCode::TileOom),
338            (0x0402, ErrorCode::TileTimeout),
339            (0x0403, ErrorCode::TileInvalidMsg),
340            (0x0404, ErrorCode::TileUnsupportedOp),
341            (0x0500, ErrorCode::KeyNotFound),
342            (0x0501, ErrorCode::KeyExpired),
343            (0x0502, ErrorCode::DecryptFailed),
344            (0x0503, ErrorCode::AlgoUnsupported),
345            (0x0504, ErrorCode::AttestationInvalid),
346            (0x0505, ErrorCode::PlatformUnsupported),
347            (0x0506, ErrorCode::AttestationExpired),
348            (0x0507, ErrorCode::KeyNotBound),
349            (0x0600, ErrorCode::ParentNotFound),
350            (0x0601, ErrorCode::ParentHashMismatch),
351            (0x0602, ErrorCode::LineageBroken),
352            (0x0603, ErrorCode::LineageCyclic),
353            (0x0800, ErrorCode::UnsignedManifest),
354            (0x0801, ErrorCode::ContentHashMismatch),
355            (0x0802, ErrorCode::UnknownSigner),
356            (0x0803, ErrorCode::EpochDriftExceeded),
357            (0x0804, ErrorCode::Level1InvalidSignature),
358            (0x0900, ErrorCode::QualityBelowThreshold),
359            (0x0901, ErrorCode::BudgetTokensExhausted),
360            (0x0902, ErrorCode::QueryBlacklisted),
361            (0x0700, ErrorCode::CowMapCorrupt),
362            (0x0701, ErrorCode::ClusterNotFound),
363            (0x0702, ErrorCode::ParentChainBroken),
364            (0x0703, ErrorCode::DeltaThresholdExceeded),
365            (0x0704, ErrorCode::SnapshotFrozen),
366            (0x0705, ErrorCode::MembershipInvalid),
367            (0x0706, ErrorCode::GenerationStale),
368            (0x0707, ErrorCode::KernelBindingMismatch),
369            (0x0708, ErrorCode::DoubleRootCorrupt),
370        ];
371        for &(raw, expected) in codes {
372            assert_eq!(ErrorCode::try_from(raw), Ok(expected), "code 0x{raw:04X}");
373            assert_eq!(expected as u16, raw);
374        }
375    }
376
377    #[test]
378    fn unknown_code() {
379        assert_eq!(ErrorCode::try_from(0x9999), Err(0x9999));
380    }
381
382    #[test]
383    fn category_extraction() {
384        assert_eq!(ErrorCode::Ok.category(), 0x00);
385        assert_eq!(ErrorCode::InvalidMagic.category(), 0x01);
386        assert_eq!(ErrorCode::DimensionMismatch.category(), 0x02);
387        assert_eq!(ErrorCode::LockHeld.category(), 0x03);
388        assert_eq!(ErrorCode::TileTrap.category(), 0x04);
389        assert_eq!(ErrorCode::KeyNotFound.category(), 0x05);
390        assert_eq!(ErrorCode::ParentNotFound.category(), 0x06);
391        assert_eq!(ErrorCode::CowMapCorrupt.category(), 0x07);
392        assert_eq!(ErrorCode::UnsignedManifest.category(), 0x08);
393        assert_eq!(ErrorCode::QualityBelowThreshold.category(), 0x09);
394    }
395
396    #[test]
397    fn security_error_check() {
398        assert!(ErrorCode::UnsignedManifest.is_security_error());
399        assert!(!ErrorCode::Ok.is_security_error());
400    }
401
402    #[test]
403    fn quality_error_check() {
404        assert!(ErrorCode::QualityBelowThreshold.is_quality_error());
405        assert!(!ErrorCode::Ok.is_quality_error());
406    }
407
408    #[test]
409    fn success_check() {
410        assert!(ErrorCode::Ok.is_success());
411        assert!(ErrorCode::OkPartial.is_success());
412        assert!(!ErrorCode::InvalidMagic.is_success());
413    }
414
415    #[test]
416    fn format_error_check() {
417        assert!(ErrorCode::InvalidMagic.is_format_error());
418        assert!(!ErrorCode::Ok.is_format_error());
419        assert!(!ErrorCode::DimensionMismatch.is_format_error());
420    }
421
422    #[test]
423    fn rvf_error_display() {
424        let e = RvfError::BadMagic {
425            expected: 0x52564653,
426            got: 0x00000000,
427        };
428        let s = format!("{e}");
429        assert!(s.contains("bad magic"));
430        assert!(s.contains("52564653"));
431    }
432
433    #[test]
434    fn cow_error_check() {
435        assert_eq!(ErrorCode::CowMapCorrupt as u16, 0x0700);
436        assert_eq!(ErrorCode::ClusterNotFound as u16, 0x0701);
437        assert_eq!(ErrorCode::ParentChainBroken as u16, 0x0702);
438        assert_eq!(ErrorCode::DeltaThresholdExceeded as u16, 0x0703);
439        assert_eq!(ErrorCode::SnapshotFrozen as u16, 0x0704);
440        assert_eq!(ErrorCode::MembershipInvalid as u16, 0x0705);
441        assert_eq!(ErrorCode::GenerationStale as u16, 0x0706);
442        assert_eq!(ErrorCode::KernelBindingMismatch as u16, 0x0707);
443        assert_eq!(ErrorCode::DoubleRootCorrupt as u16, 0x0708);
444        // All COW errors should be category 0x07
445        assert_eq!(ErrorCode::CowMapCorrupt.category(), 0x07);
446        assert_eq!(ErrorCode::DoubleRootCorrupt.category(), 0x07);
447    }
448
449    #[test]
450    fn error_codes_match_spec() {
451        assert_eq!(ErrorCode::InvalidMagic as u16, 0x0100);
452        assert_eq!(ErrorCode::InvalidChecksum as u16, 0x0102);
453        assert_eq!(ErrorCode::ManifestNotFound as u16, 0x0106);
454        assert_eq!(ErrorCode::AlgoUnsupported as u16, 0x0503);
455    }
456}