Skip to main content

llm_agent_runtime/
error.rs

1//! # Unified error type for the agent-runtime crate.
2//!
3//! ## Responsibility
4//! Provide a single, typed error enum that covers all subsystems:
5//! memory, graph, orchestration, and the ReAct agent loop.
6//!
7//! ## Guarantees
8//! - Every variant is named and carries structured context
9//! - Implements `std::error::Error` via `thiserror`
10//! - Safe to send across thread/task boundaries (`Send + Sync`)
11//! - Never panics
12
13/// Unified error type returned by all public `agent-runtime` APIs.
14#[derive(Debug, thiserror::Error)]
15pub enum AgentRuntimeError {
16    /// A memory subsystem operation failed (episodic, semantic, or working memory).
17    #[error("Memory operation failed: {0}")]
18    Memory(String),
19
20    /// A graph subsystem operation failed (entity/relationship management or traversal).
21    #[error("Graph operation failed: {0}")]
22    Graph(String),
23
24    /// The orchestration pipeline or one of its stages failed.
25    #[error("Orchestration failed: {0}")]
26    Orchestration(String),
27
28    /// The ReAct agent loop encountered an unrecoverable error.
29    #[error("Agent loop error: {0}")]
30    AgentLoop(String),
31
32    /// The runtime was used before a required subsystem was configured.
33    #[error("Runtime not configured: missing '{0}'")]
34    NotConfigured(&'static str),
35
36    /// Circuit breaker is open — fast-fail without attempting the operation.
37    #[error("Circuit breaker open for '{service}'")]
38    CircuitOpen {
39        /// Name of the service whose circuit breaker is open.
40        service: String,
41    },
42
43    /// Backpressure threshold exceeded — caller must shed or wait.
44    #[error("Backpressure threshold exceeded: queue depth {depth}/{capacity}")]
45    BackpressureShed {
46        /// Current in-flight request count at the time of rejection.
47        depth: usize,
48        /// Maximum allowed in-flight request count.
49        capacity: usize,
50    },
51
52    /// A deduplication key collision was detected.
53    #[error("Deduplication key collision: {key}")]
54    DeduplicationConflict {
55        /// The duplicated request key.
56        key: String,
57    },
58
59    /// An LLM provider call failed.
60    #[error("Provider error: {0}")]
61    Provider(String),
62
63    /// A persistence operation failed.
64    #[error("Persistence error: {0}")]
65    Persistence(String),
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn test_memory_error_display() {
74        let e = AgentRuntimeError::Memory("store full".into());
75        assert_eq!(e.to_string(), "Memory operation failed: store full");
76    }
77
78    #[test]
79    fn test_graph_error_display() {
80        let e = AgentRuntimeError::Graph("entity not found".into());
81        assert_eq!(e.to_string(), "Graph operation failed: entity not found");
82    }
83
84    #[test]
85    fn test_orchestration_error_display() {
86        let e = AgentRuntimeError::Orchestration("pipeline stalled".into());
87        assert_eq!(e.to_string(), "Orchestration failed: pipeline stalled");
88    }
89
90    #[test]
91    fn test_agent_loop_error_display() {
92        let e = AgentRuntimeError::AgentLoop("max iterations".into());
93        assert_eq!(e.to_string(), "Agent loop error: max iterations");
94    }
95
96    #[test]
97    fn test_not_configured_error_display() {
98        let e = AgentRuntimeError::NotConfigured("memory");
99        assert_eq!(e.to_string(), "Runtime not configured: missing 'memory'");
100    }
101
102    #[test]
103    fn test_circuit_open_error_display() {
104        let e = AgentRuntimeError::CircuitOpen {
105            service: "llm-api".into(),
106        };
107        assert_eq!(e.to_string(), "Circuit breaker open for 'llm-api'");
108    }
109
110    #[test]
111    fn test_backpressure_shed_error_display() {
112        let e = AgentRuntimeError::BackpressureShed {
113            depth: 100,
114            capacity: 100,
115        };
116        assert_eq!(
117            e.to_string(),
118            "Backpressure threshold exceeded: queue depth 100/100"
119        );
120    }
121
122    #[test]
123    fn test_deduplication_conflict_display() {
124        let e = AgentRuntimeError::DeduplicationConflict {
125            key: "abc123".into(),
126        };
127        assert_eq!(e.to_string(), "Deduplication key collision: abc123");
128    }
129
130    #[test]
131    fn test_error_is_send_sync() {
132        fn assert_send_sync<T: Send + Sync>() {}
133        assert_send_sync::<AgentRuntimeError>();
134    }
135
136    #[test]
137    fn test_error_debug_format() {
138        let e = AgentRuntimeError::Memory("test".into());
139        let debug = format!("{:?}", e);
140        assert!(debug.contains("Memory"));
141    }
142}