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    /// Internal logic error (count mismatch, unexpected state).
70    #[error("internal error: {0}")]
71    Internal(String),
72
73    /// A prepared SIMD operation received data at a different quantization tier.
74    /// See [`docs/design.md`](../docs/design.md#prepared-dispatch-errors) for recovery and failure semantics.
75    #[error("tier mismatch in {op}: expected {expected:?}, got {actual:?}")]
76    TierMismatch {
77        /// Name of the operation where the mismatch was detected.
78        op: &'static str,
79        /// Tier the operation required.
80        expected: crate::simd::QuantizationTier,
81        /// Tier actually supplied.
82        actual: crate::simd::QuantizationTier,
83    },
84}
85
86/// **Stable**: result type alias for embedding operations.
87pub type Result<T> = std::result::Result<T, EmbedError>;
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn test_error_display() {
95        let err = EmbedError::DimensionMismatch {
96            expected: 384,
97            actual: 768,
98        };
99        assert_eq!(err.to_string(), "dimension mismatch: expected 384, got 768");
100    }
101
102    #[test]
103    fn test_error_variants() {
104        let err = EmbedError::ModelNotLoaded("test".into());
105        assert_eq!(err.to_string(), "model not loaded: test");
106
107        let err = EmbedError::WrongModelLoaded {
108            expected: "small".into(),
109            actual: "large".into(),
110        };
111        assert!(err.to_string().contains("expected small"));
112
113        let err = EmbedError::ModelInitialization("failed".into());
114        assert!(err.to_string().contains("initialization"));
115
116        let err = EmbedError::InferenceFailed("oom".into());
117        assert!(err.to_string().contains("inference"));
118
119        let err = EmbedError::TaskFailed("panic".into());
120        assert!(err.to_string().contains("task"));
121
122        let err = EmbedError::InvalidInput("empty".into());
123        assert!(err.to_string().contains("invalid input"));
124
125        let err = EmbedError::UnsupportedModel("gpt4".into());
126        assert!(err.to_string().contains("not supported"));
127
128        let err = EmbedError::Internal("bug".into());
129        assert!(err.to_string().contains("internal"));
130
131        let err = EmbedError::TextTooLong {
132            length: 50000,
133            max: 32768,
134        };
135        assert!(err.to_string().contains("50000"));
136        assert!(err.to_string().contains("32768"));
137    }
138}