autoagents_core/agent/
error.rs1use autoagents_llm::error::LLMError;
2#[cfg(not(target_arch = "wasm32"))]
3use ractor::SpawnErr;
4use std::fmt::Debug;
5use thiserror::Error;
6
7#[derive(Debug, Error)]
9pub enum RunnableAgentError {
10 #[error("Agent execution failed: {0}")]
12 ExecutorError(String),
13
14 #[error("LLM error: {0}")]
16 LLMError(#[from] LLMError),
17
18 #[error("Task processing failed: {0}")]
20 TaskError(String),
21
22 #[error("Agent not found: {0}")]
24 AgentNotFound(uuid::Uuid),
25
26 #[error("Agent initialization failed: {0}")]
28 InitializationError(String),
29
30 #[error("Failed to send event: {0}")]
32 EventSendError(String),
33
34 #[error("Agent state error: {0}")]
36 StateError(String),
37
38 #[error("Downcast task error")]
40 DowncastTaskError,
41
42 #[error("Serialization error: {0}")]
44 SerializationError(String),
45
46 #[error("EmptyTx")]
48 EmptyTx,
49
50 #[error("Abort the execution")]
52 Abort,
53
54 #[error(transparent)]
56 Other(#[from] Box<dyn std::error::Error + Send + Sync>),
57}
58
59impl RunnableAgentError {
60 pub fn executor_error(error: impl std::error::Error) -> Self {
62 Self::ExecutorError(error.to_string())
63 }
64
65 pub fn task_error(msg: impl Into<String>) -> Self {
67 Self::TaskError(msg.into())
68 }
69
70 pub fn event_send_error(error: impl std::error::Error) -> Self {
72 Self::EventSendError(error.to_string())
73 }
74}
75
76impl From<crate::agent::executor::turn_engine::TurnEngineError> for RunnableAgentError {
77 fn from(error: crate::agent::executor::turn_engine::TurnEngineError) -> Self {
78 match error {
79 crate::agent::executor::turn_engine::TurnEngineError::LLMError(err) => {
80 RunnableAgentError::LLMError(err)
81 }
82 crate::agent::executor::turn_engine::TurnEngineError::Aborted => {
83 RunnableAgentError::Abort
84 }
85 crate::agent::executor::turn_engine::TurnEngineError::Other(err) => {
86 RunnableAgentError::ExecutorError(err)
87 }
88 }
89 }
90}
91
92impl From<crate::agent::prebuilt::executor::BasicExecutorError> for RunnableAgentError {
93 fn from(error: crate::agent::prebuilt::executor::BasicExecutorError) -> Self {
94 match error {
95 crate::agent::prebuilt::executor::BasicExecutorError::LLMError(err) => {
96 RunnableAgentError::LLMError(err)
97 }
98 crate::agent::prebuilt::executor::BasicExecutorError::Other(err) => {
99 RunnableAgentError::ExecutorError(err)
100 }
101 }
102 }
103}
104
105impl From<crate::agent::prebuilt::executor::ReActExecutorError> for RunnableAgentError {
106 fn from(error: crate::agent::prebuilt::executor::ReActExecutorError) -> Self {
107 match error {
108 crate::agent::prebuilt::executor::ReActExecutorError::LLMError(err) => {
109 RunnableAgentError::LLMError(err)
110 }
111 other => RunnableAgentError::ExecutorError(other.to_string()),
112 }
113 }
114}
115
116#[cfg(not(target_arch = "wasm32"))]
118impl<T> From<tokio::sync::mpsc::error::SendError<T>> for RunnableAgentError
119where
120 T: Debug + Send + 'static,
121{
122 fn from(error: tokio::sync::mpsc::error::SendError<T>) -> Self {
123 Self::EventSendError(error.to_string())
124 }
125}
126
127#[derive(Debug, thiserror::Error)]
128pub enum AgentBuildError {
129 #[error("Build Failure")]
130 BuildFailure(String),
131
132 #[cfg(not(target_arch = "wasm32"))]
133 #[error("SpawnError")]
134 SpawnError(#[from] SpawnErr),
135}
136
137impl AgentBuildError {
138 pub fn build_failure(msg: impl Into<String>) -> Self {
139 Self::BuildFailure(msg.into())
140 }
141}
142
143#[derive(Error, Debug)]
144pub enum AgentResultError {
145 #[error("No output available in result")]
146 NoOutput,
147
148 #[error("Failed to deserialize executor output: {0}")]
149 DeserializationError(#[from] serde_json::Error),
150
151 #[error("Agent output extraction error: {0}")]
152 AgentOutputError(String),
153}
154
155impl AgentResultError {
156 pub fn agent_output_error(msg: impl Into<String>) -> Self {
157 Self::AgentOutputError(msg.into())
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use crate::agent::prebuilt::executor::{BasicExecutorError, ReActExecutorError};
165 use autoagents_llm::error::GuardrailPhase;
166 use tokio::sync::mpsc;
167
168 #[test]
169 fn test_runnable_agent_error_display() {
170 let error = RunnableAgentError::ExecutorError("Test error".to_string());
171 assert_eq!(error.to_string(), "Agent execution failed: Test error");
172
173 let error = RunnableAgentError::TaskError("Task failed".to_string());
174 assert_eq!(error.to_string(), "Task processing failed: Task failed");
175
176 let error = RunnableAgentError::AgentNotFound(uuid::Uuid::new_v4());
177 assert!(error.to_string().contains("Agent not found:"));
178 }
179
180 #[test]
181 fn test_runnable_agent_error_constructors() {
182 let error = RunnableAgentError::executor_error(std::io::Error::other("IO error"));
183 assert!(matches!(error, RunnableAgentError::ExecutorError(_)));
184
185 let error = RunnableAgentError::task_error("Custom task error");
186 assert!(matches!(error, RunnableAgentError::TaskError(_)));
187
188 let error = RunnableAgentError::event_send_error(std::io::Error::new(
189 std::io::ErrorKind::BrokenPipe,
190 "Send failed",
191 ));
192 assert!(matches!(error, RunnableAgentError::EventSendError(_)));
193 }
194
195 #[tokio::test]
196 async fn test_runnable_agent_error_from_mpsc_send_error() {
197 let (_tx, rx) = mpsc::channel::<String>(1);
198 drop(rx); let (tx, _rx) = mpsc::channel::<String>(1);
201 drop(tx); let result: Result<(), mpsc::error::SendError<String>> =
205 Err(mpsc::error::SendError("test message".to_string()));
206
207 if let Err(send_error) = result {
208 let agent_error: RunnableAgentError = send_error.into();
209 assert!(matches!(agent_error, RunnableAgentError::EventSendError(_)));
210 }
211 }
212
213 #[test]
214 fn test_agent_build_error_display() {
215 let error = AgentBuildError::BuildFailure("Failed to build agent".to_string());
216 assert_eq!(error.to_string(), "Build Failure");
217
218 let error = AgentBuildError::build_failure("Custom build failure");
219 assert!(matches!(error, AgentBuildError::BuildFailure(_)));
220 }
221
222 #[test]
223 fn test_agent_result_error_display() {
224 let error = AgentResultError::NoOutput;
225 assert_eq!(error.to_string(), "No output available in result");
226
227 let error = AgentResultError::AgentOutputError("Custom output error".to_string());
228 assert_eq!(
229 error.to_string(),
230 "Agent output extraction error: Custom output error"
231 );
232
233 let error = AgentResultError::agent_output_error("Helper constructor error");
234 assert!(matches!(error, AgentResultError::AgentOutputError(_)));
235 }
236
237 #[test]
238 fn test_agent_result_error_from_json_error() {
239 let invalid_json = "{ invalid json }";
240 let json_error: Result<serde_json::Value, serde_json::Error> =
241 serde_json::from_str(invalid_json);
242
243 if let Err(json_err) = json_error {
244 let agent_error: AgentResultError = json_err.into();
245 assert!(matches!(
246 agent_error,
247 AgentResultError::DeserializationError(_)
248 ));
249 }
250 }
251
252 #[test]
253 fn test_error_debug_formatting() {
254 let error = RunnableAgentError::InitializationError("Init failed".to_string());
255 let debug_str = format!("{error:?}");
256 assert!(debug_str.contains("InitializationError"));
257 assert!(debug_str.contains("Init failed"));
258 }
259
260 #[test]
261 fn test_from_llm_error_preserves_typed_llm_error_direct() {
262 let source = LLMError::GuardrailBlocked {
263 phase: GuardrailPhase::Input,
264 guard: "prompt-injection".to_string().into(),
265 rule_id: "prompt_injection_detected".to_string().into(),
266 category: "prompt_injection".to_string().into(),
267 severity: "high".to_string().into(),
268 message: "detected suspicious instruction pattern: jailbreak"
269 .to_string()
270 .into(),
271 };
272
273 let converted: RunnableAgentError = source.into();
274 assert!(matches!(
275 converted,
276 RunnableAgentError::LLMError(LLMError::GuardrailBlocked { .. })
277 ));
278 }
279
280 #[test]
281 fn test_from_basic_executor_preserves_typed_llm_error() {
282 let wrapped = BasicExecutorError::from(LLMError::GuardrailBlocked {
283 phase: GuardrailPhase::Input,
284 guard: "prompt-injection".to_string().into(),
285 rule_id: "prompt_injection_detected".to_string().into(),
286 category: "prompt_injection".to_string().into(),
287 severity: "high".to_string().into(),
288 message: "detected suspicious instruction pattern: jailbreak"
289 .to_string()
290 .into(),
291 });
292
293 let converted: RunnableAgentError = wrapped.into();
294 assert!(matches!(
295 converted,
296 RunnableAgentError::LLMError(LLMError::GuardrailBlocked { .. })
297 ));
298 }
299
300 #[test]
301 fn test_from_react_executor_preserves_typed_llm_error() {
302 let wrapped = ReActExecutorError::from(LLMError::GuardrailBlocked {
303 phase: GuardrailPhase::Input,
304 guard: "prompt-injection".to_string().into(),
305 rule_id: "prompt_injection_detected".to_string().into(),
306 category: "prompt_injection".to_string().into(),
307 severity: "high".to_string().into(),
308 message: "detected suspicious instruction pattern: jailbreak"
309 .to_string()
310 .into(),
311 });
312
313 let converted: RunnableAgentError = wrapped.into();
314 assert!(matches!(
315 converted,
316 RunnableAgentError::LLMError(LLMError::GuardrailBlocked { .. })
317 ));
318 }
319}