Skip to main content

oxi_agent/
error.rs

1//! Error types for oxi-agent
2
3/// Agent-specific errors
4#[derive(Debug, thiserror::Error)]
5pub enum AgentError {
6    /// Error originating from a tool.
7    #[error("Tool '{tool_name}' failed: {message}")]
8    Tool {
9        /// Name of the tool that failed.
10        tool_name: String,
11        /// Human-readable error message.
12        message: String,
13    },
14    /// Stream / provider communication error
15    #[error("Stream error: {0}")]
16    Stream(String),
17    /// State management error
18    #[error("State error: {0}")]
19    State(String),
20    /// Configuration error
21    #[error("Config error: {0}")]
22    Config(String),
23    /// Model not found or unavailable.
24    #[error("Model '{model_id}' error: {message}")]
25    Model {
26        /// Identifier of the model.
27        model_id: String,
28        /// Error detail.
29        message: String,
30    },
31    /// Maximum iterations reached.
32    #[error("Maximum iterations reached ({iterations})")]
33    MaxIterations {
34        /// Number of iterations that were executed.
35        iterations: usize,
36    },
37    /// Rate limited – retry after N seconds.
38    #[error("Rate limited – retry after {retry_after_secs}s")]
39    RateLimited {
40        /// Seconds to wait before retrying.
41        retry_after_secs: u64,
42    },
43    /// A retriable error that failed after exhausting retries.
44    #[error("Failed after {attempts} retries: {last_error}")]
45    RetriesExhausted {
46        /// Number of retry attempts made.
47        attempts: usize,
48        /// Error from the final attempt.
49        last_error: String,
50    },
51    /// Fallback failed – both primary and fallback model errored
52    #[error("Both models failed – {primary_model} ({primary_error}) and {fallback_model} ({fallback_error})")]
53    FallbackFailed {
54        /// Name of the primary model.
55        primary_model: String,
56        /// Error from the primary model.
57        primary_error: String,
58        /// Name of the fallback model.
59        fallback_model: String,
60        /// Error from the fallback model.
61        fallback_error: String,
62    },
63}
64
65impl AgentError {
66    /// Check if this error is retryable.
67    pub fn is_retryable(&self) -> bool {
68        matches!(
69            self,
70            Self::RateLimited { .. } | Self::Stream(_) | Self::RetriesExhausted { .. }
71        )
72    }
73
74    /// Produce a short, user-friendly message suitable for TUI display.
75    pub fn user_friendly(&self) -> String {
76        match self {
77            Self::Tool { tool_name, message } => {
78                format!("Tool '{}' failed: {}", tool_name, message)
79            }
80            Self::Stream(msg) => format!("Connection error: {}", msg),
81            Self::State(msg) => format!("Internal error: {}", msg),
82            Self::Config(msg) => format!("Configuration error: {}", msg),
83            Self::Model { model_id, message } => {
84                format!("Model '{}' error: {}", model_id, message)
85            }
86            Self::MaxIterations { iterations } => {
87                format!(
88                    "Reached the iteration limit ({}). Try simplifying your request.",
89                    iterations
90                )
91            }
92            Self::RateLimited { retry_after_secs } => {
93                format!("Rate limited – will retry in {}s", retry_after_secs)
94            }
95            Self::RetriesExhausted {
96                attempts,
97                last_error,
98            } => {
99                format!("Failed after {} attempts: {}", attempts, last_error)
100            }
101            Self::FallbackFailed {
102                primary_model,
103                primary_error,
104                fallback_model,
105                fallback_error: _,
106            } => {
107                format!(
108                    "Primary model ({}) failed: {}. Fallback ({}) also failed.",
109                    primary_model, primary_error, fallback_model
110                )
111            }
112        }
113    }
114}
115
116impl From<anyhow::Error> for AgentError {
117    fn from(err: anyhow::Error) -> Self {
118        AgentError::Stream(err.to_string())
119    }
120}
121
122/// Result type alias for agent operations.
123pub type Result<T> = std::result::Result<T, AgentError>;