gemini_memory_rs/core/
error.rs1use thiserror::Error;
8
9use super::ids::MemoryId;
10
11#[derive(Debug, Error)]
13pub enum MemoryError {
14 #[error("malformed OKF record at {path}: {message}")]
16 MalformedRecord {
17 path: String,
19 message: String,
21 },
22
23 #[error("memory {0} not found")]
25 NotFound(MemoryId),
26
27 #[error("write conflict: expected revision {expected}, found {actual}")]
29 RevisionConflict {
30 expected: u64,
32 actual: u64,
34 },
35
36 #[error("extraction failed: {0}")]
38 Extraction(String),
39
40 #[error("retrieval failed: {0}")]
42 Retrieval(String),
43
44 #[error("reconciliation failed: {0}")]
46 Reconciliation(String),
47
48 #[error("event log unavailable: {0}")]
50 EventLog(String),
51
52 #[error("refused by policy: {0}")]
54 PolicyRefused(String),
55
56 #[error("storage error: {0}")]
58 Storage(String),
59
60 #[error("{operation} exceeded its {budget_ms}ms budget")]
62 DeadlineExceeded {
63 operation: &'static str,
65 budget_ms: u64,
67 },
68}
69
70impl MemoryError {
71 pub fn is_retryable(&self) -> bool {
73 matches!(
74 self,
75 Self::Extraction(_)
76 | Self::Retrieval(_)
77 | Self::Reconciliation(_)
78 | Self::EventLog(_)
79 | Self::Storage(_)
80 | Self::DeadlineExceeded { .. }
81 | Self::RevisionConflict { .. }
82 )
83 }
84
85 pub fn should_degrade_silently(&self) -> bool {
90 !matches!(self, Self::PolicyRefused(_))
91 }
92}
93
94impl From<std::io::Error> for MemoryError {
95 fn from(value: std::io::Error) -> Self {
96 Self::Storage(value.to_string())
97 }
98}
99
100impl From<serde_json::Error> for MemoryError {
101 fn from(value: serde_json::Error) -> Self {
102 Self::Storage(value.to_string())
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn transient_failures_are_retryable_but_policy_refusals_are_not() {
112 assert!(MemoryError::Retrieval("timeout".into()).is_retryable());
113 assert!(!MemoryError::PolicyRefused("sensitive".into()).is_retryable());
114 }
115
116 #[test]
117 fn only_policy_refusals_surface_to_the_caller() {
118 assert!(MemoryError::EventLog("down".into()).should_degrade_silently());
119 assert!(!MemoryError::PolicyRefused("restricted".into()).should_degrade_silently());
120 }
121}