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