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
/// Error types for the semantic-memory crate.
///
/// All errors flow through [`MemoryError`], using `#[from]` for automatic
/// conversion from rusqlite and reqwest errors.
#[derive(Debug, thiserror::Error)]
pub enum MemoryError {
/// SQLite / rusqlite error.
#[error("Database error: {0}")]
Database(#[from] rusqlite::Error),
/// HTTP error from the embedding provider.
#[error("Embedding request failed: {0}")]
EmbeddingRequest(#[from] reqwest::Error),
/// Embedding vector has wrong number of dimensions.
#[error("Embedding provider returned {actual} dimensions, expected {expected}")]
DimensionMismatch { expected: usize, actual: usize },
/// Raw BLOB data is not a valid embedding.
#[error("Invalid embedding data: expected {expected_bytes} bytes, got {actual_bytes}")]
InvalidEmbedding {
expected_bytes: usize,
actual_bytes: usize,
},
/// Database was created with a different embedding model.
#[error("Embedding model mismatch: database has '{stored}', config specifies '{configured}'")]
ModelMismatch { stored: String, configured: String },
/// Session with the given ID does not exist.
#[error("Session not found: {0}")]
SessionNotFound(String),
/// Fact with the given ID does not exist.
#[error("Fact not found: {0}")]
FactNotFound(String),
/// Document with the given ID does not exist.
#[error("Document not found: {0}")]
DocumentNotFound(String),
/// Embedding provider is unreachable or misconfigured.
#[error("Embedding provider unavailable: {0}")]
EmbedderUnavailable(String),
/// Database migration failed.
#[error("Migration failed at version {version}: {reason}")]
MigrationFailed { version: u32, reason: String },
/// HNSW index error.
#[error("HNSW index error: {0}")]
HnswError(String),
/// Invalid HNSW key format.
#[error("Invalid HNSW key format: {0}")]
InvalidKey(String),
/// Quantization error.
#[error("Quantization error: {0}")]
QuantizationError(String),
/// Storage path error.
#[error("Storage path error: {0}")]
StorageError(String),
/// Index integrity check failed.
#[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")]
IntegrityError {
in_sqlite_not_hnsw: usize,
in_hnsw_not_sqlite: usize,
},
/// Database schema is newer than this library version can handle.
#[error(
"Schema version {found} is ahead of max supported {supported} — upgrade semantic-memory"
)]
SchemaAhead {
/// Schema version found in the database.
found: u32,
/// Maximum version supported by this build.
supported: u32,
},
/// Content exceeds configured size limit.
#[error("Content too large: {size} bytes exceeds limit of {limit} bytes")]
ContentTooLarge {
/// Actual content size in bytes.
size: usize,
/// Configured limit in bytes.
limit: usize,
},
/// Namespace fact count would exceed the configured limit.
#[error("Namespace '{namespace}' has {count} facts, limit is {limit}")]
NamespaceFull {
/// Namespace that is full.
namespace: String,
/// Current fact count.
count: usize,
/// Configured limit.
limit: usize,
},
/// The configured database size ceiling would be exceeded by a new write.
#[error("Database size limit exceeded: current footprint is {current} bytes, limit is {limit} bytes")]
DatabaseSizeLimitExceeded {
/// Current observed database footprint in bytes.
current: u64,
/// Configured limit in bytes.
limit: u64,
},
/// Episode with the given ID does not exist.
#[error("Episode not found: {0}")]
EpisodeNotFound(String),
/// Connection pool reader acquisition timed out.
#[error("Pool reader acquisition timed out after {elapsed_ms}ms (pool size: {pool_size})")]
PoolTimeout {
/// How long the caller waited before giving up.
elapsed_ms: u64,
/// Number of reader slots in the pool.
pool_size: usize,
},
/// Configuration could not be normalized into a valid runtime state.
#[error("Invalid configuration for '{field}': {reason}")]
InvalidConfig {
/// The config field or section that failed validation.
field: &'static str,
/// Human-readable explanation of the invalid value.
reason: String,
},
/// Stored data is malformed or internally inconsistent.
#[error("Corrupt data in {table} ({row_id}): {detail}")]
CorruptData {
/// Table or logical collection containing the bad row.
table: &'static str,
/// Primary key / row identifier for the corrupt record.
row_id: String,
/// Human-readable description of the corruption.
detail: String,
},
/// Import envelope is structurally invalid.
#[error("Invalid import envelope: {reason}")]
ImportInvalid {
/// What is wrong with the envelope.
reason: String,
},
/// Import envelope has already been ingested (idempotent duplicate).
#[error("Import envelope already ingested: {envelope_id}")]
ImportDuplicate {
/// The duplicate envelope ID.
envelope_id: String,
},
/// Import hit a historical digest/receipt drift seam and needs operator repair.
#[error(
"Import requires digest migration or receipt repair for {source_envelope_id}: {detail}"
)]
ImportMigrationRequired {
/// The source envelope whose historical import receipts no longer line up.
source_envelope_id: String,
/// Human-readable conflict details and operator guidance.
detail: String,
},
/// Catch-all for other errors.
#[error("{0}")]
Other(String),
}
impl MemoryError {
/// Returns a stable string discriminant for programmatic matching.
pub fn kind(&self) -> &'static str {
match self {
Self::Database(_) => "database",
Self::EmbeddingRequest(_) => "embedding_request",
Self::DimensionMismatch { .. } => "dimension_mismatch",
Self::InvalidEmbedding { .. } => "invalid_embedding",
Self::ModelMismatch { .. } => "model_mismatch",
Self::SessionNotFound(_) => "session_not_found",
Self::FactNotFound(_) => "fact_not_found",
Self::DocumentNotFound(_) => "document_not_found",
Self::EpisodeNotFound(_) => "episode_not_found",
Self::PoolTimeout { .. } => "pool_timeout",
Self::EmbedderUnavailable(_) => "embedder_unavailable",
Self::MigrationFailed { .. } => "migration_failed",
Self::HnswError(_) => "hnsw_error",
Self::InvalidKey(_) => "invalid_key",
Self::QuantizationError(_) => "quantization_error",
Self::StorageError(_) => "storage_error",
Self::IntegrityError { .. } => "integrity_error",
Self::SchemaAhead { .. } => "schema_ahead",
Self::ContentTooLarge { .. } => "content_too_large",
Self::NamespaceFull { .. } => "namespace_full",
Self::DatabaseSizeLimitExceeded { .. } => "database_size_limit_exceeded",
Self::InvalidConfig { .. } => "invalid_config",
Self::CorruptData { .. } => "corrupt_data",
Self::ImportInvalid { .. } => "import_invalid",
Self::ImportDuplicate { .. } => "import_duplicate",
Self::ImportMigrationRequired { .. } => "import_migration_required",
Self::Other(_) => "other",
}
}
}