Skip to main content

velesdb_core/
error.rs

1//! Error types for `VelesDB`.
2//!
3//! This module provides a unified error type for all `VelesDB` operations,
4//! designed for professional API exposure to Python/Node clients.
5
6use thiserror::Error;
7
8/// Result type alias for `VelesDB` operations.
9pub type Result<T> = std::result::Result<T, Error>;
10
11/// Errors that can occur in `VelesDB` operations.
12///
13/// Each variant includes a descriptive error message suitable for end-users.
14/// Error codes follow the pattern `VELES-XXX` for easy debugging.
15#[derive(Error, Debug)]
16#[non_exhaustive]
17pub enum Error {
18    /// Collection already exists (VELES-001).
19    #[error("[VELES-001] Collection '{0}' already exists")]
20    CollectionExists(String),
21
22    /// Collection not found (VELES-002).
23    #[error("[VELES-002] Collection '{0}' not found")]
24    CollectionNotFound(String),
25
26    /// Point not found (VELES-003).
27    #[error("[VELES-003] Point with ID '{0}' not found")]
28    PointNotFound(u64),
29
30    /// Dimension mismatch (VELES-004).
31    #[error("[VELES-004] Vector dimension mismatch: expected {expected}, got {actual}")]
32    DimensionMismatch {
33        /// Expected dimension.
34        expected: usize,
35        /// Actual dimension.
36        actual: usize,
37    },
38
39    /// Invalid vector (VELES-005).
40    #[error("[VELES-005] Invalid vector: {0}")]
41    InvalidVector(String),
42
43    /// Storage error (VELES-006).
44    #[error("[VELES-006] Storage error: {0}")]
45    Storage(String),
46
47    /// Index error (VELES-007).
48    #[error("[VELES-007] Index error: {0}")]
49    Index(String),
50
51    /// Index corrupted (VELES-008).
52    ///
53    /// Indicates that index files are corrupted and need to be rebuilt.
54    #[error("[VELES-008] Index corrupted: {0}")]
55    IndexCorrupted(String),
56
57    /// Configuration error (VELES-009).
58    #[error("[VELES-009] Configuration error: {0}")]
59    Config(String),
60
61    /// Query parsing error (VELES-010).
62    ///
63    /// Wraps `VelesQL` parse errors with position and context information.
64    #[error("[VELES-010] Query error: {0}")]
65    Query(String),
66
67    /// IO error (VELES-011).
68    #[error("[VELES-011] IO error: {0}")]
69    Io(#[from] std::io::Error),
70
71    /// Serialization error (VELES-012).
72    #[error("[VELES-012] Serialization error: {0}")]
73    Serialization(String),
74
75    /// Internal error (VELES-013).
76    ///
77    /// Indicates an unexpected internal error. Please report if encountered.
78    #[error("[VELES-013] Internal error: {0}")]
79    Internal(String),
80
81    /// Vector not allowed on metadata-only collection (VELES-014).
82    #[error("[VELES-014] Vector not allowed on metadata-only collection '{0}'")]
83    VectorNotAllowed(String),
84
85    /// Search not supported on metadata-only collection (VELES-015).
86    #[error("[VELES-015] Vector search not supported on metadata-only collection '{0}'. Use query() instead.")]
87    SearchNotSupported(String),
88
89    /// Vector required for vector collection (VELES-016).
90    #[error("[VELES-016] Vector required for collection '{0}' (not metadata-only)")]
91    VectorRequired(String),
92
93    /// Schema validation error (VELES-017).
94    #[error("[VELES-017] Schema validation error: {0}")]
95    SchemaValidation(String),
96
97    /// Graph operation not supported (VELES-018).
98    #[error("[VELES-018] Graph operation not supported: {0}")]
99    GraphNotSupported(String),
100
101    /// Edge already exists (VELES-019).
102    #[error("[VELES-019] Edge with ID '{0}' already exists")]
103    EdgeExists(u64),
104
105    /// Edge not found (VELES-020).
106    #[error("[VELES-020] Edge with ID '{0}' not found")]
107    EdgeNotFound(u64),
108
109    /// Invalid edge label (VELES-021).
110    #[error("[VELES-021] Invalid edge label: {0}")]
111    InvalidEdgeLabel(String),
112
113    /// Node not found (VELES-022).
114    #[error("[VELES-022] Node with ID '{0}' not found")]
115    NodeNotFound(u64),
116
117    /// Numeric overflow (VELES-023).
118    ///
119    /// Indicates a numeric conversion would overflow or truncate.
120    /// Use `try_from()` instead of `as` casts for user-provided data.
121    #[error("[VELES-023] Numeric overflow: {0}")]
122    Overflow(String),
123
124    /// Column store error (VELES-024).
125    ///
126    /// Indicates a column store schema or primary key validation failure.
127    #[error("[VELES-024] Column store error: {0}")]
128    ColumnStoreError(String),
129
130    /// GPU operation error (VELES-025).
131    ///
132    /// Indicates a GPU parameter validation or operation failure.
133    #[error("[VELES-025] GPU error: {0}")]
134    GpuError(String),
135
136    /// Epoch mismatch (VELES-026).
137    ///
138    /// Indicates a stale mmap guard detected after a remap operation.
139    /// This is not recoverable — the guard must be re-acquired.
140    #[error("[VELES-026] Epoch mismatch: {0}")]
141    EpochMismatch(String),
142
143    /// Guard-rail violation (VELES-027).
144    ///
145    /// A query exceeded a configured limit (timeout, depth, cardinality,
146    /// memory, rate limit, or circuit breaker).
147    #[error("[VELES-027] Guard-rail violation: {0}")]
148    GuardRail(String),
149
150    /// Invalid quantizer configuration (VELES-028).
151    ///
152    /// Indicates invalid parameters passed to a quantizer (e.g., empty training set,
153    /// zero subspaces, dimension not divisible by subspaces).
154    #[error("[VELES-028] Invalid quantizer config: {0}")]
155    InvalidQuantizerConfig(String),
156
157    /// Training failed (VELES-029).
158    ///
159    /// Indicates a quantizer training operation failed (convergence, insufficient
160    /// data, etc.).
161    #[error("[VELES-029] Training failed: {0}")]
162    TrainingFailed(String),
163
164    /// Sparse index error (VELES-030).
165    #[error("[VELES-030] Sparse index error: {0}")]
166    SparseIndexError(String),
167
168    /// Database already locked by another process (VELES-031).
169    #[error("[VELES-031] Database is already opened by another process: {0}")]
170    DatabaseLocked(String),
171
172    /// Invalid dimension (VELES-032).
173    ///
174    /// Indicates a vector dimension outside the valid range.
175    #[error("[VELES-032] Invalid dimension {dimension}: must be between {min} and {max}")]
176    InvalidDimension {
177        /// The invalid dimension provided.
178        dimension: usize,
179        /// Minimum valid dimension.
180        min: usize,
181        /// Maximum valid dimension.
182        max: usize,
183    },
184
185    /// Allocation failed (VELES-033).
186    ///
187    /// Indicates a memory allocation failure (out of memory or invalid layout).
188    #[error("[VELES-033] Allocation failed: {0}")]
189    AllocationFailed(String),
190
191    /// Invalid collection name (VELES-034).
192    ///
193    /// The collection name contains forbidden characters, path separators,
194    /// or is otherwise unsafe for use as a filesystem directory name.
195    #[error("[VELES-034] Invalid collection name '{name}': {reason}")]
196    InvalidCollectionName {
197        /// The rejected name.
198        name: String,
199        /// Human-readable explanation of why it was rejected.
200        reason: String,
201    },
202
203    /// CSR snapshot build failed (VELES-035).
204    ///
205    /// Indicates that building a CSR snapshot from the edge store failed
206    /// (e.g., allocation failure during rebuild).
207    #[error("[VELES-035] Snapshot build failed: {0}")]
208    SnapshotBuildFailed(String),
209
210    /// Incompatible schema version (VELES-036).
211    ///
212    /// The collection was created with a newer `VelesDB` version that uses a
213    /// schema format this version cannot read. Upgrade `VelesDB` to open it.
214    #[error(
215        "[VELES-036] Collection created with newer schema version (v{found}), \
216         current VelesDB supports up to v{supported}"
217    )]
218    IncompatibleSchemaVersion {
219        /// The schema version found in `config.json`.
220        found: u32,
221        /// The maximum schema version this binary supports.
222        supported: u32,
223    },
224}
225
226impl Error {
227    /// Returns the error code (e.g., "VELES-001").
228    #[must_use]
229    pub const fn code(&self) -> &'static str {
230        match self {
231            Self::CollectionExists(_) => "VELES-001",
232            Self::CollectionNotFound(_) => "VELES-002",
233            Self::PointNotFound(_) => "VELES-003",
234            Self::DimensionMismatch { .. } => "VELES-004",
235            Self::InvalidVector(_) => "VELES-005",
236            Self::Storage(_) => "VELES-006",
237            Self::Index(_) => "VELES-007",
238            Self::IndexCorrupted(_) => "VELES-008",
239            Self::Config(_) => "VELES-009",
240            Self::Query(_) => "VELES-010",
241            Self::Io(_) => "VELES-011",
242            Self::Serialization(_) => "VELES-012",
243            Self::Internal(_) => "VELES-013",
244            Self::VectorNotAllowed(_) => "VELES-014",
245            Self::SearchNotSupported(_) => "VELES-015",
246            Self::VectorRequired(_) => "VELES-016",
247            Self::SchemaValidation(_) => "VELES-017",
248            Self::GraphNotSupported(_) => "VELES-018",
249            Self::EdgeExists(_) => "VELES-019",
250            Self::EdgeNotFound(_) => "VELES-020",
251            Self::InvalidEdgeLabel(_) => "VELES-021",
252            Self::NodeNotFound(_) => "VELES-022",
253            Self::Overflow(_) => "VELES-023",
254            Self::ColumnStoreError(_) => "VELES-024",
255            Self::GpuError(_) => "VELES-025",
256            Self::EpochMismatch(_) => "VELES-026",
257            Self::GuardRail(_) => "VELES-027",
258            Self::InvalidQuantizerConfig(_) => "VELES-028",
259            Self::TrainingFailed(_) => "VELES-029",
260            Self::SparseIndexError(_) => "VELES-030",
261            Self::DatabaseLocked(_) => "VELES-031",
262            Self::InvalidDimension { .. } => "VELES-032",
263            Self::AllocationFailed(_) => "VELES-033",
264            Self::InvalidCollectionName { .. } => "VELES-034",
265            Self::SnapshotBuildFailed(_) => "VELES-035",
266            Self::IncompatibleSchemaVersion { .. } => "VELES-036",
267        }
268    }
269
270    /// Returns true if this error is recoverable.
271    ///
272    /// Non-recoverable errors include corruption and internal errors.
273    #[must_use]
274    pub const fn is_recoverable(&self) -> bool {
275        !matches!(
276            self,
277            Self::IndexCorrupted(_)
278                | Self::Internal(_)
279                | Self::EpochMismatch(_)
280                | Self::AllocationFailed(_)
281                | Self::IncompatibleSchemaVersion { .. }
282        )
283    }
284}
285
286/// Conversion from `VelesQL` `ParseError`.
287impl From<crate::velesql::ParseError> for Error {
288    fn from(err: crate::velesql::ParseError) -> Self {
289        Self::Query(err.to_string())
290    }
291}
292
293/// Conversion from `GuardRailViolation` — surfaces limit violations as query errors.
294#[cfg(feature = "persistence")]
295impl From<crate::guardrails::GuardRailViolation> for Error {
296    fn from(v: crate::guardrails::GuardRailViolation) -> Self {
297        Self::GuardRail(v.to_string())
298    }
299}