Skip to main content

lattice_embed/
error.rs

1//! Errors returned by embedding, model configuration, and prepared SIMD-dispatch operations.
2//!
3//! See `docs/service.md` for error boundaries and caller recovery guidance.
4
5use thiserror::Error;
6
7/// **Stable**: external consumers may depend on this; breaking changes require a SemVer bump.
8///
9/// Errors that can occur during embedding operations.
10#[derive(Error, Debug)]
11#[non_exhaustive]
12pub enum EmbedError {
13    /// Model not loaded (needs initialization).
14    #[error("model not loaded: {0}")]
15    ModelNotLoaded(String),
16
17    /// Wrong model loaded (concurrent model switch in progress).
18    ///
19    /// This can happen when multiple tasks request different models concurrently.
20    /// The caller should retry with backoff.
21    #[error("wrong model loaded: expected {expected}, got {actual}")]
22    WrongModelLoaded {
23        /// Model that was expected.
24        expected: String,
25        /// Model that was actually loaded.
26        actual: String,
27    },
28
29    /// Model initialization failed.
30    #[error("model initialization failed: {0}")]
31    ModelInitialization(String),
32
33    /// Embedding inference failed.
34    #[error("embedding inference failed: {0}")]
35    InferenceFailed(String),
36
37    /// Blocking task failed (panic or cancellation).
38    ///
39    /// The model cache may be lost; next call will reinitialize.
40    #[error("task execution failed: {0}")]
41    TaskFailed(String),
42
43    /// Invalid input provided.
44    #[error("invalid input: {0}")]
45    InvalidInput(String),
46
47    /// Input text exceeds maximum allowed length.
48    #[error("text too long: {length} bytes exceeds maximum {max} bytes")]
49    TextTooLong {
50        /// Actual length in UTF-8 bytes.
51        length: usize,
52        /// Maximum allowed length in UTF-8 bytes.
53        max: usize,
54    },
55
56    /// Dimension mismatch between expected and actual.
57    #[error("dimension mismatch: expected {expected}, got {actual}")]
58    DimensionMismatch {
59        /// Expected dimension.
60        expected: usize,
61        /// Actual dimension.
62        actual: usize,
63    },
64
65    /// Model not supported by this service.
66    #[error("model not supported: {0}")]
67    UnsupportedModel(String),
68
69    /// Operation not implemented by this service.
70    ///
71    /// Returned by the default bodies of trait methods that an implementation has not
72    /// overridden. The message names the service and the operation.
73    #[error("operation not supported: {0}")]
74    Unsupported(String),
75
76    /// An attestation report is outside the closed public byte bound.
77    #[error("attestation report size {length} bytes is outside {min}..={max}")]
78    AttestationReportSize {
79        /// Actual report length in bytes.
80        length: usize,
81        /// Smallest accepted report length in bytes.
82        min: usize,
83        /// Largest accepted report length in bytes.
84        max: usize,
85    },
86
87    /// The independent retained and transient-work pools overflow aggregate accounting.
88    #[error(
89        "resource budget overflow: retained {retained_bytes} bytes plus transient work {transient_work_bytes} bytes"
90    )]
91    ResourceBudgetOverflow {
92        /// Retained-pool byte ceiling.
93        retained_bytes: u64,
94        /// Transient-work-pool byte ceiling.
95        transient_work_bytes: u64,
96    },
97
98    /// Internal logic error (count mismatch, unexpected state).
99    #[error("internal error: {0}")]
100    Internal(String),
101
102    /// A prepared SIMD operation received data at a different quantization tier.
103    /// See [`docs/design.md`](../docs/design.md#prepared-dispatch-errors) for recovery and failure semantics.
104    #[error("tier mismatch in {op}: expected {expected:?}, got {actual:?}")]
105    TierMismatch {
106        /// Name of the operation where the mismatch was detected.
107        op: &'static str,
108        /// Tier the operation required.
109        expected: crate::simd::QuantizationTier,
110        /// Tier actually supplied.
111        actual: crate::simd::QuantizationTier,
112    },
113}
114
115/// **Stable**: result type alias for embedding operations.
116pub type Result<T> = std::result::Result<T, EmbedError>;
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn test_error_display() {
124        let err = EmbedError::DimensionMismatch {
125            expected: 384,
126            actual: 768,
127        };
128        assert_eq!(err.to_string(), "dimension mismatch: expected 384, got 768");
129    }
130
131    #[test]
132    fn test_error_variants() {
133        let err = EmbedError::ModelNotLoaded("test".into());
134        assert_eq!(err.to_string(), "model not loaded: test");
135
136        let err = EmbedError::WrongModelLoaded {
137            expected: "small".into(),
138            actual: "large".into(),
139        };
140        assert!(err.to_string().contains("expected small"));
141
142        let err = EmbedError::ModelInitialization("failed".into());
143        assert!(err.to_string().contains("initialization"));
144
145        let err = EmbedError::InferenceFailed("oom".into());
146        assert!(err.to_string().contains("inference"));
147
148        let err = EmbedError::TaskFailed("panic".into());
149        assert!(err.to_string().contains("task"));
150
151        let err = EmbedError::InvalidInput("empty".into());
152        assert!(err.to_string().contains("invalid input"));
153
154        let err = EmbedError::UnsupportedModel("gpt4".into());
155        assert!(err.to_string().contains("not supported"));
156
157        let err = EmbedError::Unsupported("probe does not implement count_tokens".into());
158        assert_eq!(
159            err.to_string(),
160            "operation not supported: probe does not implement count_tokens"
161        );
162
163        let err = EmbedError::Internal("bug".into());
164        assert!(err.to_string().contains("internal"));
165
166        let err = EmbedError::TextTooLong {
167            length: 50000,
168            max: 32768,
169        };
170        assert!(err.to_string().contains("50000"));
171        assert!(err.to_string().contains("32768"));
172    }
173}