Skip to main content

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}