1#[derive(Debug, thiserror::Error)]
6pub enum MemoryError {
7 #[error("Database error: {0}")]
9 Database(#[from] rusqlite::Error),
10
11 #[error("Embedding request failed: {0}")]
13 EmbeddingRequest(#[from] reqwest::Error),
14
15 #[cfg(feature = "candle-embedder")]
17 #[error("Candle error: {0}")]
18 CandleError(#[from] candle_core::Error),
19
20 #[error("Embedding provider returned {actual} dimensions, expected {expected}")]
22 DimensionMismatch { expected: usize, actual: usize },
23
24 #[error("Embedding provider returned {returned} vectors, expected {requested}")]
26 EmbeddingBatchCountMismatch { requested: usize, returned: usize },
27
28 #[error("Embedding vector has {actual} dimensions, expected {expected}")]
30 EmbeddingDimensionMismatch { expected: usize, actual: usize },
31
32 #[error("Embedding vector contains non-finite value at index {index}")]
34 NonFiniteEmbeddingValue { index: usize },
35
36 #[error("Vector blob length mismatch: expected {expected_bytes} bytes, got {actual_bytes}")]
38 VectorBlobLengthMismatch {
39 expected_bytes: usize,
40 actual_bytes: usize,
41 },
42
43 #[error("Vector codec profile mismatch: expected {expected_digest}, got {actual_digest}")]
45 VectorCodecProfileMismatch {
46 expected_digest: String,
48 actual_digest: String,
50 },
51
52 #[error("Search receipt ID conflict for {receipt_id}")]
54 SearchReceiptConflict {
55 receipt_id: String,
57 },
58
59 #[error("Digest error: {0}")]
61 DigestError(String),
62
63 #[error("Search receipt not found: {receipt_id}")]
65 SearchReceiptNotFound {
66 receipt_id: String,
68 },
69
70 #[error("Invalid embedding data: expected {expected_bytes} bytes, got {actual_bytes}")]
72 InvalidEmbedding {
73 expected_bytes: usize,
74 actual_bytes: usize,
75 },
76
77 #[error("Embedding model mismatch: database has '{stored}', config specifies '{configured}'")]
79 ModelMismatch { stored: String, configured: String },
80
81 #[error("Session not found: {0}")]
83 SessionNotFound(String),
84
85 #[error("Fact not found: {0}")]
87 FactNotFound(String),
88
89 #[error("Document not found: {0}")]
91 DocumentNotFound(String),
92
93 #[error("Embedding provider unavailable: {0}")]
95 EmbedderUnavailable(String),
96
97 #[error("Migration failed at version {version}: {reason}")]
99 MigrationFailed { version: u32, reason: String },
100
101 #[error("HNSW index error: {0}")]
103 HnswError(String),
104
105 #[error("Not implemented: {0}")]
107 NotImplemented(String),
108
109 #[error("Invalid HNSW key format: {0}")]
111 InvalidKey(String),
112
113 #[error("Quantization error: {0}")]
115 QuantizationError(String),
116
117 #[error("Storage path error: {0}")]
119 StorageError(String),
120
121 #[error("Index integrity check failed: {in_sqlite_not_hnsw} items in SQLite but not HNSW, {in_hnsw_not_sqlite} items in HNSW but not SQLite")]
123 IntegrityError {
124 in_sqlite_not_hnsw: usize,
125 in_hnsw_not_sqlite: usize,
126 },
127
128 #[error(
130 "Schema version {found} is ahead of max supported {supported} — upgrade semantic-memory"
131 )]
132 SchemaAhead {
133 found: u32,
135 supported: u32,
137 },
138
139 #[error("Content too large: {size} bytes exceeds limit of {limit} bytes")]
141 ContentTooLarge {
142 size: usize,
144 limit: usize,
146 },
147
148 #[error("Namespace '{namespace}' has {count} facts, limit is {limit}")]
150 NamespaceFull {
151 namespace: String,
153 count: usize,
155 limit: usize,
157 },
158
159 #[error("Database size limit exceeded: current footprint is {current} bytes, limit is {limit} bytes")]
161 DatabaseSizeLimitExceeded {
162 current: u64,
164 limit: u64,
166 },
167
168 #[error("Episode not found: {0}")]
170 EpisodeNotFound(String),
171
172 #[error("Pool reader acquisition timed out after {elapsed_ms}ms (pool size: {pool_size})")]
174 PoolTimeout {
175 elapsed_ms: u64,
177 pool_size: usize,
179 },
180
181 #[error(
183 "Vector scan hard limit exceeded for {table}: scanned {scanned} rows, limit is {limit}"
184 )]
185 VectorScanLimitExceeded {
186 table: String,
188 scanned: usize,
190 limit: usize,
192 },
193
194 #[error("Invalid configuration for '{field}': {reason}")]
196 InvalidConfig {
197 field: &'static str,
199 reason: String,
201 },
202
203 #[error("Corrupt data in {table} ({row_id}): {detail}")]
205 CorruptData {
206 table: &'static str,
208 row_id: String,
210 detail: String,
212 },
213
214 #[error("Invalid import envelope: {reason}")]
216 ImportInvalid {
217 reason: String,
219 },
220
221 #[error("Import envelope already ingested: {envelope_id}")]
223 ImportDuplicate {
224 envelope_id: String,
226 },
227
228 #[error(
230 "Import requires digest migration or receipt repair for {source_envelope_id}: {detail}"
231 )]
232 ImportMigrationRequired {
233 source_envelope_id: String,
235 detail: String,
237 },
238
239 #[error("{0}")]
241 Other(String),
242}
243
244impl MemoryError {
245 pub fn kind(&self) -> &'static str {
247 match self {
248 Self::Database(_) => "database",
249 Self::EmbeddingRequest(_) => "embedding_request",
250 #[cfg(feature = "candle-embedder")]
251 Self::CandleError(_) => "candle_error",
252 Self::DimensionMismatch { .. } => "dimension_mismatch",
253 Self::EmbeddingBatchCountMismatch { .. } => "embedding_batch_count_mismatch",
254 Self::EmbeddingDimensionMismatch { .. } => "embedding_dimension_mismatch",
255 Self::NonFiniteEmbeddingValue { .. } => "non_finite_embedding_value",
256 Self::VectorBlobLengthMismatch { .. } => "vector_blob_length_mismatch",
257 Self::VectorCodecProfileMismatch { .. } => "vector_codec_profile_mismatch",
258 Self::SearchReceiptConflict { .. } => "search_receipt_conflict",
259 Self::DigestError(_) => "digest_error",
260 Self::SearchReceiptNotFound { .. } => "search_receipt_not_found",
261 Self::InvalidEmbedding { .. } => "invalid_embedding",
262 Self::ModelMismatch { .. } => "model_mismatch",
263 Self::SessionNotFound(_) => "session_not_found",
264 Self::FactNotFound(_) => "fact_not_found",
265 Self::DocumentNotFound(_) => "document_not_found",
266 Self::EpisodeNotFound(_) => "episode_not_found",
267 Self::PoolTimeout { .. } => "pool_timeout",
268 Self::VectorScanLimitExceeded { .. } => "vector_scan_limit_exceeded",
269 Self::EmbedderUnavailable(_) => "embedder_unavailable",
270 Self::MigrationFailed { .. } => "migration_failed",
271 Self::HnswError(_) => "hnsw_error",
272 Self::NotImplemented(_) => "not_implemented",
273 Self::InvalidKey(_) => "invalid_key",
274 Self::QuantizationError(_) => "quantization_error",
275 Self::StorageError(_) => "storage_error",
276 Self::IntegrityError { .. } => "integrity_error",
277 Self::SchemaAhead { .. } => "schema_ahead",
278 Self::ContentTooLarge { .. } => "content_too_large",
279 Self::NamespaceFull { .. } => "namespace_full",
280 Self::DatabaseSizeLimitExceeded { .. } => "database_size_limit_exceeded",
281 Self::InvalidConfig { .. } => "invalid_config",
282 Self::CorruptData { .. } => "corrupt_data",
283 Self::ImportInvalid { .. } => "import_invalid",
284 Self::ImportDuplicate { .. } => "import_duplicate",
285 Self::ImportMigrationRequired { .. } => "import_migration_required",
286 Self::Other(_) => "other",
287 }
288 }
289}