Skip to main content

agent_runtime/
error.rs

1use std::fmt;
2
3/// Main error type for the agent runtime system
4#[derive(Debug, Clone)]
5pub enum RuntimeError {
6    /// Error during workflow execution
7    Workflow(WorkflowError),
8
9    /// Error during agent execution
10    Agent(AgentError),
11
12    /// Error from LLM provider
13    Llm(LlmError),
14
15    /// Error during tool execution
16    Tool(ToolError),
17
18    /// Configuration validation error
19    Config(ConfigError),
20
21    /// Retry attempts exhausted
22    RetryExhausted {
23        operation: String,
24        attempts: u32,
25        last_error: Box<RuntimeError>,
26    },
27
28    /// Operation timed out
29    Timeout { operation: String, duration_ms: u64 },
30}
31
32/// Workflow-specific errors
33#[derive(Debug, Clone)]
34pub struct WorkflowError {
35    pub code: WorkflowErrorCode,
36    pub message: String,
37    pub step_id: Option<String>,
38    pub context: Option<String>,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum WorkflowErrorCode {
43    StepExecutionFailed,
44    InvalidStepOutput,
45    CycleDetected,
46    MaxIterationsExceeded,
47    ConditionalEvaluationFailed,
48}
49
50/// Agent-specific errors
51#[derive(Debug, Clone)]
52pub struct AgentError {
53    pub code: AgentErrorCode,
54    pub message: String,
55    pub agent_name: Option<String>,
56    pub context: Option<String>,
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum AgentErrorCode {
61    ExecutionFailed,
62    InvalidInput,
63    InvalidOutput,
64    ToolExecutionFailed,
65    MaxToolIterationsExceeded,
66    MissingLlmClient,
67    MissingSystemPrompt,
68}
69
70/// LLM provider errors
71#[derive(Debug, Clone)]
72pub struct LlmError {
73    pub code: LlmErrorCode,
74    pub message: String,
75    pub provider: Option<String>,
76    pub model: Option<String>,
77    pub retryable: bool,
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub enum LlmErrorCode {
82    NetworkError,
83    AuthenticationFailed,
84    RateLimitExceeded,
85    InvalidRequest,
86    InvalidResponse,
87    ModelNotFound,
88    ContextLengthExceeded,
89    ServerError,
90    ParseError,
91}
92
93/// Tool execution errors
94#[derive(Debug, Clone)]
95pub struct ToolError {
96    pub code: ToolErrorCode,
97    pub message: String,
98    pub tool_name: Option<String>,
99    pub context: Option<String>,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub enum ToolErrorCode {
104    InvalidParameters,
105    ExecutionFailed,
106    Timeout,
107    NotFound,
108    McpConnectionFailed,
109    McpToolCallFailed,
110}
111
112/// Configuration validation errors
113#[derive(Debug, Clone)]
114pub struct ConfigError {
115    pub code: ConfigErrorCode,
116    pub message: String,
117    pub field: Option<String>,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub enum ConfigErrorCode {
122    MissingRequiredField,
123    InvalidValue,
124    ValidationFailed,
125    FileNotFound,
126    ParseError,
127}
128
129// Implement Display for all error types
130impl fmt::Display for RuntimeError {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        match self {
133            RuntimeError::Workflow(e) => write!(f, "Workflow error: {}", e),
134            RuntimeError::Agent(e) => write!(f, "Agent error: {}", e),
135            RuntimeError::Llm(e) => write!(f, "LLM error: {}", e),
136            RuntimeError::Tool(e) => write!(f, "Tool error: {}", e),
137            RuntimeError::Config(e) => write!(f, "Configuration error: {}", e),
138            RuntimeError::RetryExhausted {
139                operation,
140                attempts,
141                last_error,
142            } => {
143                write!(
144                    f,
145                    "Retry exhausted for '{}' after {} attempts: {}",
146                    operation, attempts, last_error
147                )
148            }
149            RuntimeError::Timeout {
150                operation,
151                duration_ms,
152            } => {
153                write!(
154                    f,
155                    "Operation '{}' timed out after {}ms",
156                    operation, duration_ms
157                )
158            }
159        }
160    }
161}
162
163impl fmt::Display for WorkflowError {
164    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165        write!(f, "[{:?}] {}", self.code, self.message)?;
166        if let Some(step_id) = &self.step_id {
167            write!(f, " (step: {})", step_id)?;
168        }
169        if let Some(context) = &self.context {
170            write!(f, " - {}", context)?;
171        }
172        Ok(())
173    }
174}
175
176impl fmt::Display for AgentError {
177    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178        write!(f, "[{:?}] {}", self.code, self.message)?;
179        if let Some(agent_name) = &self.agent_name {
180            write!(f, " (agent: {})", agent_name)?;
181        }
182        if let Some(context) = &self.context {
183            write!(f, " - {}", context)?;
184        }
185        Ok(())
186    }
187}
188
189impl fmt::Display for LlmError {
190    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191        write!(f, "[{:?}] {}", self.code, self.message)?;
192        if let Some(provider) = &self.provider {
193            write!(f, " (provider: {})", provider)?;
194        }
195        if let Some(model) = &self.model {
196            write!(f, " [model: {}]", model)?;
197        }
198        if self.retryable {
199            write!(f, " (retryable)")?;
200        }
201        Ok(())
202    }
203}
204
205impl fmt::Display for ToolError {
206    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207        write!(f, "[{:?}] {}", self.code, self.message)?;
208        if let Some(tool_name) = &self.tool_name {
209            write!(f, " (tool: {})", tool_name)?;
210        }
211        if let Some(context) = &self.context {
212            write!(f, " - {}", context)?;
213        }
214        Ok(())
215    }
216}
217
218impl fmt::Display for ConfigError {
219    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220        write!(f, "[{:?}] {}", self.code, self.message)?;
221        if let Some(field) = &self.field {
222            write!(f, " (field: {})", field)?;
223        }
224        Ok(())
225    }
226}
227
228// Implement std::error::Error
229impl std::error::Error for RuntimeError {}
230impl std::error::Error for WorkflowError {}
231impl std::error::Error for AgentError {}
232impl std::error::Error for LlmError {}
233impl std::error::Error for ToolError {}
234impl std::error::Error for ConfigError {}
235
236// Helper methods for LlmError
237impl LlmError {
238    /// Check if this error is retryable (network issues, rate limits, server errors)
239    pub fn is_retryable(&self) -> bool {
240        matches!(
241            self.code,
242            LlmErrorCode::NetworkError
243                | LlmErrorCode::RateLimitExceeded
244                | LlmErrorCode::ServerError
245        )
246    }
247
248    /// Create a network error
249    pub fn network(message: impl Into<String>) -> Self {
250        Self {
251            code: LlmErrorCode::NetworkError,
252            message: message.into(),
253            provider: None,
254            model: None,
255            retryable: true,
256        }
257    }
258
259    /// Create a rate limit error
260    pub fn rate_limit(message: impl Into<String>) -> Self {
261        Self {
262            code: LlmErrorCode::RateLimitExceeded,
263            message: message.into(),
264            provider: None,
265            model: None,
266            retryable: true,
267        }
268    }
269
270    /// Create a server error
271    pub fn server_error(message: impl Into<String>) -> Self {
272        Self {
273            code: LlmErrorCode::ServerError,
274            message: message.into(),
275            provider: None,
276            model: None,
277            retryable: true,
278        }
279    }
280}
281
282// Conversion helpers
283impl From<WorkflowError> for RuntimeError {
284    fn from(e: WorkflowError) -> Self {
285        RuntimeError::Workflow(e)
286    }
287}
288
289impl From<AgentError> for RuntimeError {
290    fn from(e: AgentError) -> Self {
291        RuntimeError::Agent(e)
292    }
293}
294
295impl From<LlmError> for RuntimeError {
296    fn from(e: LlmError) -> Self {
297        RuntimeError::Llm(e)
298    }
299}
300
301impl From<ToolError> for RuntimeError {
302    fn from(e: ToolError) -> Self {
303        RuntimeError::Tool(e)
304    }
305}
306
307impl From<ConfigError> for RuntimeError {
308    fn from(e: ConfigError) -> Self {
309        RuntimeError::Config(e)
310    }
311}