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 #[error("Embedding provider returned {actual} dimensions, expected {expected}")]
17 DimensionMismatch { expected: usize, actual: usize },
18
19 #[error("Embedding provider returned {returned} vectors, expected {requested}")]
21 EmbeddingBatchCountMismatch { requested: usize, returned: usize },
22
23 #[error("Embedding vector has {actual} dimensions, expected {expected}")]
25 EmbeddingDimensionMismatch { expected: usize, actual: usize },
26
27 #[error("Embedding vector contains non-finite value at index {index}")]
29 NonFiniteEmbeddingValue { index: usize },
30
31 #[error("Vector blob length mismatch: expected {expected_bytes} bytes, got {actual_bytes}")]
33 VectorBlobLengthMismatch {
34 expected_bytes: usize,
35 actual_bytes: usize,
36 },
37
38 #[error("Vector codec profile mismatch: expected {expected_digest}, got {actual_digest}")]
40 VectorCodecProfileMismatch {
41 expected_digest: String,
43 actual_digest: String,
45 },
46
47 #[error("Search receipt ID conflict for {receipt_id}")]
49 SearchReceiptConflict {
50 receipt_id: String,
52 },
53
54 #[error("Digest error: {0}")]
56 DigestError(String),
57
58 #[error("Search receipt not found: {receipt_id}")]
60 SearchReceiptNotFound {
61 receipt_id: String,
63 },
64
65 #[error("Invalid embedding data: expected {expected_bytes} bytes, got {actual_bytes}")]
67 InvalidEmbedding {
68 expected_bytes: usize,
69 actual_bytes: usize,
70 },
71
72 #[error("Embedding model mismatch: database has '{stored}', config specifies '{configured}'")]
74 ModelMismatch { stored: String, configured: String },
75
76 #[error("Session not found: {0}")]
78 SessionNotFound(String),
79
80 #[error("Fact not found: {0}")]
82 FactNotFound(String),
83
84 #[error("Document not found: {0}")]
86 DocumentNotFound(String),
87
88 #[error("Embedding provider unavailable: {0}")]
90 EmbedderUnavailable(String),
91
92 #[error("Migration failed at version {version}: {reason}")]
94 MigrationFailed { version: u32, reason: String },
95
96 #[error("HNSW index error: {0}")]
98 HnswError(String),
99
100 #[error("Not implemented: {0}")]
102 NotImplemented(String),
103
104 #[error("Invalid HNSW key format: {0}")]
106 InvalidKey(String),
107
108 #[error("Quantization error: {0}")]
110 QuantizationError(String),
111
112 #[error("Storage path error: {0}")]
114 StorageError(String),
115
116 #[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")]
118 IntegrityError {
119 in_sqlite_not_hnsw: usize,
120 in_hnsw_not_sqlite: usize,
121 },
122
123 #[error(
125 "Schema version {found} is ahead of max supported {supported} — upgrade semantic-memory"
126 )]
127 SchemaAhead {
128 found: u32,
130 supported: u32,
132 },
133
134 #[error("Content too large: {size} bytes exceeds limit of {limit} bytes")]
136 ContentTooLarge {
137 size: usize,
139 limit: usize,
141 },
142
143 #[error("Namespace '{namespace}' has {count} facts, limit is {limit}")]
145 NamespaceFull {
146 namespace: String,
148 count: usize,
150 limit: usize,
152 },
153
154 #[error("Database size limit exceeded: current footprint is {current} bytes, limit is {limit} bytes")]
156 DatabaseSizeLimitExceeded {
157 current: u64,
159 limit: u64,
161 },
162
163 #[error("Episode not found: {0}")]
165 EpisodeNotFound(String),
166
167 #[error("Pool reader acquisition timed out after {elapsed_ms}ms (pool size: {pool_size})")]
169 PoolTimeout {
170 elapsed_ms: u64,
172 pool_size: usize,
174 },
175
176 #[error(
178 "Vector scan hard limit exceeded for {table}: scanned {scanned} rows, limit is {limit}"
179 )]
180 VectorScanLimitExceeded {
181 table: String,
183 scanned: usize,
185 limit: usize,
187 },
188
189 #[error("Invalid configuration for '{field}': {reason}")]
191 InvalidConfig {
192 field: &'static str,
194 reason: String,
196 },
197
198 #[error("Corrupt data in {table} ({row_id}): {detail}")]
200 CorruptData {
201 table: &'static str,
203 row_id: String,
205 detail: String,
207 },
208
209 #[error("Invalid import envelope: {reason}")]
211 ImportInvalid {
212 reason: String,
214 },
215
216 #[error("Import envelope already ingested: {envelope_id}")]
218 ImportDuplicate {
219 envelope_id: String,
221 },
222
223 #[error(
225 "Import requires digest migration or receipt repair for {source_envelope_id}: {detail}"
226 )]
227 ImportMigrationRequired {
228 source_envelope_id: String,
230 detail: String,
232 },
233
234 #[error("{0}")]
236 Other(String),
237}
238
239impl MemoryError {
240 pub fn kind(&self) -> &'static str {
242 match self {
243 Self::Database(_) => "database",
244 Self::EmbeddingRequest(_) => "embedding_request",
245 Self::DimensionMismatch { .. } => "dimension_mismatch",
246 Self::EmbeddingBatchCountMismatch { .. } => "embedding_batch_count_mismatch",
247 Self::EmbeddingDimensionMismatch { .. } => "embedding_dimension_mismatch",
248 Self::NonFiniteEmbeddingValue { .. } => "non_finite_embedding_value",
249 Self::VectorBlobLengthMismatch { .. } => "vector_blob_length_mismatch",
250 Self::VectorCodecProfileMismatch { .. } => "vector_codec_profile_mismatch",
251 Self::SearchReceiptConflict { .. } => "search_receipt_conflict",
252 Self::DigestError(_) => "digest_error",
253 Self::SearchReceiptNotFound { .. } => "search_receipt_not_found",
254 Self::InvalidEmbedding { .. } => "invalid_embedding",
255 Self::ModelMismatch { .. } => "model_mismatch",
256 Self::SessionNotFound(_) => "session_not_found",
257 Self::FactNotFound(_) => "fact_not_found",
258 Self::DocumentNotFound(_) => "document_not_found",
259 Self::EpisodeNotFound(_) => "episode_not_found",
260 Self::PoolTimeout { .. } => "pool_timeout",
261 Self::VectorScanLimitExceeded { .. } => "vector_scan_limit_exceeded",
262 Self::EmbedderUnavailable(_) => "embedder_unavailable",
263 Self::MigrationFailed { .. } => "migration_failed",
264 Self::HnswError(_) => "hnsw_error",
265 Self::NotImplemented(_) => "not_implemented",
266 Self::InvalidKey(_) => "invalid_key",
267 Self::QuantizationError(_) => "quantization_error",
268 Self::StorageError(_) => "storage_error",
269 Self::IntegrityError { .. } => "integrity_error",
270 Self::SchemaAhead { .. } => "schema_ahead",
271 Self::ContentTooLarge { .. } => "content_too_large",
272 Self::NamespaceFull { .. } => "namespace_full",
273 Self::DatabaseSizeLimitExceeded { .. } => "database_size_limit_exceeded",
274 Self::InvalidConfig { .. } => "invalid_config",
275 Self::CorruptData { .. } => "corrupt_data",
276 Self::ImportInvalid { .. } => "import_invalid",
277 Self::ImportDuplicate { .. } => "import_duplicate",
278 Self::ImportMigrationRequired { .. } => "import_migration_required",
279 Self::Other(_) => "other",
280 }
281 }
282}