velesdb_memory/error.rs
1//! Error type for the memory layer.
2
3use velesdb_core::agent::AgentMemoryError;
4use velesdb_core::Error as CoreError;
5
6use crate::embedder::EmbedError;
7use crate::extract::ExtractError;
8
9/// The transport-neutral class of a [`MemoryError`] — the single source of
10/// truth every adapter maps onto its own error channel (JSON-RPC code, napi
11/// status, `PyO3` exception type), so the taxonomy can never drift between them.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ErrorCategory {
14 /// The caller supplied bad input (empty fact, reserved key, malformed
15 /// filter) — a 4xx-style fault.
16 InvalidInput,
17 /// A referenced memory id does not exist.
18 NotFound,
19 /// An internal storage / embedding / extraction failure — a 5xx-style fault.
20 Internal,
21}
22
23/// Errors returned by [`crate::service::MemoryService`].
24#[derive(Debug, thiserror::Error)]
25pub enum MemoryError {
26 /// Failure in the underlying `VelesDB` storage engine.
27 #[error("storage error: {0}")]
28 Storage(#[from] CoreError),
29
30 /// Failure in the Agent Memory SDK.
31 #[error("memory error: {0}")]
32 Memory(#[from] AgentMemoryError),
33
34 /// A fact was empty or whitespace-only.
35 #[error("fact text must not be empty")]
36 EmptyFact,
37
38 /// A `remember` link or a `relate` endpoint referenced a memory id that
39 /// does not exist.
40 #[error("memory {0} does not exist")]
41 UnknownMemory(u64),
42
43 /// Caller metadata or a recall filter named a reserved key (`content` or a
44 /// `_veles_`-prefixed system key), which callers may not set or filter on.
45 #[error("metadata key '{0}' is reserved")]
46 ReservedKey(String),
47
48 /// Failure producing a text embedding.
49 #[error("embedding error: {0}")]
50 Embed(#[from] EmbedError),
51
52 /// Failure extracting facts from raw text in
53 /// [`crate::service::MemoryService::remember_extracted`].
54 #[error("extraction error: {0}")]
55 Extract(#[from] ExtractError),
56
57 /// A fused-recall filter referenced a field name that is not a plain
58 /// identifier, named a reserved key, or carried a non-scalar value.
59 #[error("invalid filter field: {0}")]
60 InvalidFilter(String),
61}
62
63impl MemoryError {
64 /// Classify this error into a transport-neutral [`ErrorCategory`]. Adapters
65 /// map the *category*, not the variant, so the client-facing taxonomy stays
66 /// identical across the MCP server and every binding.
67 #[must_use]
68 pub fn category(&self) -> ErrorCategory {
69 match self {
70 Self::EmptyFact | Self::ReservedKey(_) | Self::InvalidFilter(_) => {
71 ErrorCategory::InvalidInput
72 }
73 Self::UnknownMemory(_) => ErrorCategory::NotFound,
74 Self::Storage(_) | Self::Memory(_) | Self::Embed(_) | Self::Extract(_) => {
75 ErrorCategory::Internal
76 }
77 }
78 }
79}