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(feature = "codeact")]
117impl From<crate::agent::prebuilt::executor::CodeActExecutorError> for RunnableAgentError {
118 fn from(error: crate::agent::prebuilt::executor::CodeActExecutorError) -> Self {
119 match error {
120 crate::agent::prebuilt::executor::CodeActExecutorError::LLMError(err) => {
121 RunnableAgentError::LLMError(err)
122 }
123 other => RunnableAgentError::ExecutorError(other.to_string()),
124 }
125 }
126}
127
128#[cfg(not(target_arch = "wasm32"))]
130impl<T> From<tokio::sync::mpsc::error::SendError<T>> for RunnableAgentError
131where
132 T: Debug + Send + 'static,
133{
134 fn from(error: tokio::sync::mpsc::error::SendError<T>) -> Self {
135 Self::EventSendError(error.to_string())
136 }
137}
138
139#[derive(Debug, thiserror::Error)]
140pub enum AgentBuildError {
141 #[error("Build Failure")]
142 BuildFailure(String),
143
144 #[cfg(not(target_arch = "wasm32"))]
145 #[error("SpawnError")]
146 SpawnError(#[from] SpawnErr),
147}
148
149impl AgentBuildError {
150 pub fn build_failure(msg: impl Into<String>) -> Self {
151 Self::BuildFailure(msg.into())
152 }
153}
154
155#[derive(Error, Debug)]
156pub enum AgentResultError {
157 #[error("No output available in result")]
158 NoOutput,
159
160 #[error("Failed to deserialize executor output: {0}")]
161 DeserializationError(#[from] serde_json::Error),
162
163 #[error("Agent output extraction error: {0}")]
164 AgentOutputError(String),
165}
166
167impl AgentResultError {
168 pub fn agent_output_error(msg: impl Into<String>) -> Self {
169 Self::AgentOutputError(msg.into())
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176 use crate::agent::prebuilt::executor::{BasicExecutorError, ReActExecutorError};
177 use autoagents_llm::error::GuardrailPhase;
178 use tokio::sync::mpsc;
179
180 #[test]
181 fn test_runnable_agent_error_display() {
182 let error = RunnableAgentError::ExecutorError("Test error".to_string());
183 assert_eq!(error.to_string(), "Agent execution failed: Test error");
184
185 let error = RunnableAgentError::TaskError("Task failed".to_string());
186 assert_eq!(error.to_string(), "Task processing failed: Task failed");
187
188 let error = RunnableAgentError::AgentNotFound(uuid::Uuid::new_v4());
189 assert!(error.to_string().contains("Agent not found:"));
190 }
191
192 #[test]
193 fn test_runnable_agent_error_constructors() {
194 let error = RunnableAgentError::executor_error(std::io::Error::other("IO error"));
195 assert!(matches!(error, RunnableAgentError::ExecutorError(_)));
196
197 let error = RunnableAgentError::task_error("Custom task error");
198 assert!(matches!(error, RunnableAgentError::TaskError(_)));
199
200 let error = RunnableAgentError::event_send_error(std::io::Error::new(
201 std::io::ErrorKind::BrokenPipe,
202 "Send failed",
203 ));
204 assert!(matches!(error, RunnableAgentError::EventSendError(_)));
205 }
206
207 #[tokio::test]
208 async fn test_runnable_agent_error_from_mpsc_send_error() {
209 let (_tx, rx) = mpsc::channel::<String>(1);
210 drop(rx); let (tx, _rx) = mpsc::channel::<String>(1);
213 drop(tx); let result: Result<(), mpsc::error::SendError<String>> =
217 Err(mpsc::error::SendError("test message".to_string()));
218
219 if let Err(send_error) = result {
220 let agent_error: RunnableAgentError = send_error.into();
221 assert!(matches!(agent_error, RunnableAgentError::EventSendError(_)));
222 }
223 }
224
225 #[test]
226 fn test_agent_build_error_display() {
227 let error = AgentBuildError::BuildFailure("Failed to build agent".to_string());
228 assert_eq!(error.to_string(), "Build Failure");
229
230 let error = AgentBuildError::build_failure("Custom build failure");
231 assert!(matches!(error, AgentBuildError::BuildFailure(_)));
232 }
233
234 #[test]
235 fn test_agent_result_error_display() {
236 let error = AgentResultError::NoOutput;
237 assert_eq!(error.to_string(), "No output available in result");
238
239 let error = AgentResultError::AgentOutputError("Custom output error".to_string());
240 assert_eq!(
241 error.to_string(),
242 "Agent output extraction error: Custom output error"
243 );
244
245 let error = AgentResultError::agent_output_error("Helper constructor error");
246 assert!(matches!(error, AgentResultError::AgentOutputError(_)));
247 }
248
249 #[test]
250 fn test_agent_result_error_from_json_error() {
251 let invalid_json = "{ invalid json }";
252 let json_error: Result<serde_json::Value, serde_json::Error> =
253 serde_json::from_str(invalid_json);
254
255 if let Err(json_err) = json_error {
256 let agent_error: AgentResultError = json_err.into();
257 assert!(matches!(
258 agent_error,
259 AgentResultError::DeserializationError(_)
260 ));
261 }
262 }
263
264 #[test]
265 fn test_error_debug_formatting() {
266 let error = RunnableAgentError::InitializationError("Init failed".to_string());
267 let debug_str = format!("{error:?}");
268 assert!(debug_str.contains("InitializationError"));
269 assert!(debug_str.contains("Init failed"));
270 }
271
272 #[test]
273 fn test_from_llm_error_preserves_typed_llm_error_direct() {
274 let source = LLMError::GuardrailBlocked {
275 phase: GuardrailPhase::Input,
276 guard: "prompt-injection".to_string().into(),
277 rule_id: "prompt_injection_detected".to_string().into(),
278 category: "prompt_injection".to_string().into(),
279 severity: "high".to_string().into(),
280 message: "detected suspicious instruction pattern: jailbreak"
281 .to_string()
282 .into(),
283 };
284
285 let converted: RunnableAgentError = source.into();
286 assert!(matches!(
287 converted,
288 RunnableAgentError::LLMError(LLMError::GuardrailBlocked { .. })
289 ));
290 }
291
292 #[test]
293 fn test_from_basic_executor_preserves_typed_llm_error() {
294 let wrapped = BasicExecutorError::from(LLMError::GuardrailBlocked {
295 phase: GuardrailPhase::Input,
296 guard: "prompt-injection".to_string().into(),
297 rule_id: "prompt_injection_detected".to_string().into(),
298 category: "prompt_injection".to_string().into(),
299 severity: "high".to_string().into(),
300 message: "detected suspicious instruction pattern: jailbreak"
301 .to_string()
302 .into(),
303 });
304
305 let converted: RunnableAgentError = wrapped.into();
306 assert!(matches!(
307 converted,
308 RunnableAgentError::LLMError(LLMError::GuardrailBlocked { .. })
309 ));
310 }
311
312 #[test]
313 fn test_from_react_executor_preserves_typed_llm_error() {
314 let wrapped = ReActExecutorError::from(LLMError::GuardrailBlocked {
315 phase: GuardrailPhase::Input,
316 guard: "prompt-injection".to_string().into(),
317 rule_id: "prompt_injection_detected".to_string().into(),
318 category: "prompt_injection".to_string().into(),
319 severity: "high".to_string().into(),
320 message: "detected suspicious instruction pattern: jailbreak"
321 .to_string()
322 .into(),
323 });
324
325 let converted: RunnableAgentError = wrapped.into();
326 assert!(matches!(
327 converted,
328 RunnableAgentError::LLMError(LLMError::GuardrailBlocked { .. })
329 ));
330 }
331}