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 /// A relation label supplied to [`crate::service::MemoryService::relate`] or
63 /// a [`crate::model::Link`] in
64 /// [`crate::service::MemoryService::remember`] was invalid — empty, too long,
65 /// or contained non-printable characters.
66 #[error("invalid relation label: {0}")]
67 InvalidRelation(String),
68}
69
70impl MemoryError {
71 /// Classify this error into a transport-neutral [`ErrorCategory`]. Adapters
72 /// map the *category*, not the variant, so the client-facing taxonomy stays
73 /// identical across the MCP server and every binding.
74 #[must_use]
75 pub fn category(&self) -> ErrorCategory {
76 match self {
77 Self::EmptyFact
78 | Self::ReservedKey(_)
79 | Self::InvalidFilter(_)
80 | Self::InvalidRelation(_) => ErrorCategory::InvalidInput,
81 Self::UnknownMemory(_) => ErrorCategory::NotFound,
82 Self::Storage(_) | Self::Memory(_) | Self::Embed(_) | Self::Extract(_) => {
83 ErrorCategory::Internal
84 }
85 }
86 }
87}