gemini_adk_rs/
error.rs

1//! Error types for the agent runtime.
2
3use gemini_genai_rs::session::SessionError;
4
5/// Convenience alias for fallible agent-runtime operations.
6///
7/// Lets call sites write `AgentResult<T>` instead of the more verbose
8/// `Result<T, AgentError>`.
9pub type AgentResult<T> = std::result::Result<T, AgentError>;
10
11/// Errors that can occur during agent execution.
12#[derive(Debug, thiserror::Error)]
13pub enum AgentError {
14    /// A wire-level session error (WebSocket, auth, setup).
15    #[error("Session error: {0}")]
16    Session(#[from] SessionError),
17
18    /// A tool execution error.
19    #[error("Tool error: {0}")]
20    Tool(#[from] ToolError),
21
22    /// The requested agent was not found in the registry.
23    #[error("Unknown agent: {0}")]
24    UnknownAgent(String),
25
26    /// The agent requested a transfer to another agent.
27    #[error("Transfer requested to agent: {0}")]
28    TransferRequested(String),
29
30    /// An agent transfer was attempted but failed.
31    #[error("Agent transfer failed: {0}")]
32    TransferFailed(String),
33
34    /// The underlying session has been closed.
35    #[error("Agent session closed")]
36    SessionClosed,
37
38    /// The operation timed out.
39    #[error("Timeout")]
40    Timeout,
41
42    /// A configuration error.
43    #[error("Configuration error: {0}")]
44    Config(String),
45
46    /// A catch-all for other errors.
47    #[error("{0}")]
48    Other(String),
49}
50
51/// Errors that can occur during tool execution.
52#[derive(Debug, Clone, thiserror::Error)]
53pub enum ToolError {
54    /// The tool's execution logic failed.
55    #[error("Tool execution failed: {0}")]
56    ExecutionFailed(String),
57
58    /// No tool with this name is registered.
59    #[error("Tool not found: {0}")]
60    NotFound(String),
61
62    /// The arguments provided to the tool were invalid.
63    #[error("Invalid arguments: {0}")]
64    InvalidArgs(String),
65
66    /// The tool call was cancelled before completion.
67    #[error("Tool cancelled")]
68    Cancelled,
69
70    /// The tool call exceeded its timeout.
71    #[error("Tool execution timed out after {0:?}")]
72    Timeout(std::time::Duration),
73
74    /// A catch-all for other tool errors.
75    #[error("{0}")]
76    Other(String),
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use std::time::Duration;
83
84    #[test]
85    fn agent_error_display_messages() {
86        let err = AgentError::UnknownAgent("foo".into());
87        assert_eq!(err.to_string(), "Unknown agent: foo");
88
89        let err = AgentError::TransferRequested("bar".into());
90        assert_eq!(err.to_string(), "Transfer requested to agent: bar");
91
92        let err = AgentError::TransferFailed("baz".into());
93        assert_eq!(err.to_string(), "Agent transfer failed: baz");
94
95        let err = AgentError::SessionClosed;
96        assert_eq!(err.to_string(), "Agent session closed");
97
98        let err = AgentError::Timeout;
99        assert_eq!(err.to_string(), "Timeout");
100
101        let err = AgentError::Config("bad value".into());
102        assert_eq!(err.to_string(), "Configuration error: bad value");
103
104        let err = AgentError::Other("something".into());
105        assert_eq!(err.to_string(), "something");
106    }
107
108    #[test]
109    fn agent_error_from_session_error() {
110        use gemini_genai_rs::session::SessionError;
111        use gemini_genai_rs::session::WebSocketError;
112
113        let ws_err = SessionError::WebSocket(WebSocketError::ConnectionRefused("refused".into()));
114        let agent_err: AgentError = ws_err.into();
115        let msg = agent_err.to_string();
116        assert!(msg.contains("Session error"), "got: {msg}");
117    }
118
119    #[test]
120    fn agent_error_from_tool_error() {
121        let tool_err = ToolError::NotFound("my_tool".into());
122        let agent_err: AgentError = tool_err.into();
123        let msg = agent_err.to_string();
124        assert!(msg.contains("Tool error"), "got: {msg}");
125        assert!(msg.contains("my_tool"), "got: {msg}");
126    }
127
128    #[test]
129    fn tool_error_display_messages() {
130        assert_eq!(
131            ToolError::ExecutionFailed("boom".into()).to_string(),
132            "Tool execution failed: boom"
133        );
134        assert_eq!(
135            ToolError::NotFound("x".into()).to_string(),
136            "Tool not found: x"
137        );
138        assert_eq!(
139            ToolError::InvalidArgs("bad".into()).to_string(),
140            "Invalid arguments: bad"
141        );
142        assert_eq!(ToolError::Cancelled.to_string(), "Tool cancelled");
143        assert_eq!(ToolError::Other("misc".into()).to_string(), "misc");
144    }
145
146    #[test]
147    fn tool_error_timeout_shows_duration() {
148        let err = ToolError::Timeout(Duration::from_secs(5));
149        let msg = err.to_string();
150        assert!(msg.contains("5s"), "got: {msg}");
151        assert!(msg.contains("timed out"), "got: {msg}");
152    }
153
154    #[test]
155    fn tool_error_is_clone() {
156        let err = ToolError::ExecutionFailed("test".into());
157        let cloned = err.clone();
158        assert_eq!(err.to_string(), cloned.to_string());
159
160        let err2 = ToolError::Timeout(Duration::from_millis(100));
161        let cloned2 = err2.clone();
162        assert_eq!(err2.to_string(), cloned2.to_string());
163    }
164}