use autoagents_llm::error::LLMError;
#[cfg(not(target_arch = "wasm32"))]
use ractor::SpawnErr;
use std::fmt::Debug;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum RunnableAgentError {
#[error("Agent execution failed: {0}")]
ExecutorError(String),
#[error("LLM error: {0}")]
LLMError(#[from] LLMError),
#[error("Task processing failed: {0}")]
TaskError(String),
#[error("Agent not found: {0}")]
AgentNotFound(uuid::Uuid),
#[error("Agent initialization failed: {0}")]
InitializationError(String),
#[error("Failed to send event: {0}")]
EventSendError(String),
#[error("Agent state error: {0}")]
StateError(String),
#[error("Downcast task error")]
DowncastTaskError,
#[error("Serialization error: {0}")]
SerializationError(String),
#[error("EmptyTx")]
EmptyTx,
#[error("Abort the execution")]
Abort,
#[error(transparent)]
Other(#[from] Box<dyn std::error::Error + Send + Sync>),
}
impl RunnableAgentError {
pub fn executor_error(error: impl std::error::Error) -> Self {
Self::ExecutorError(error.to_string())
}
pub fn task_error(msg: impl Into<String>) -> Self {
Self::TaskError(msg.into())
}
pub fn event_send_error(error: impl std::error::Error) -> Self {
Self::EventSendError(error.to_string())
}
}
impl From<crate::agent::executor::turn_engine::TurnEngineError> for RunnableAgentError {
fn from(error: crate::agent::executor::turn_engine::TurnEngineError) -> Self {
match error {
crate::agent::executor::turn_engine::TurnEngineError::LLMError(err) => {
RunnableAgentError::LLMError(err)
}
crate::agent::executor::turn_engine::TurnEngineError::Aborted => {
RunnableAgentError::Abort
}
crate::agent::executor::turn_engine::TurnEngineError::Other(err) => {
RunnableAgentError::ExecutorError(err)
}
}
}
}
impl From<crate::agent::prebuilt::executor::BasicExecutorError> for RunnableAgentError {
fn from(error: crate::agent::prebuilt::executor::BasicExecutorError) -> Self {
match error {
crate::agent::prebuilt::executor::BasicExecutorError::LLMError(err) => {
RunnableAgentError::LLMError(err)
}
crate::agent::prebuilt::executor::BasicExecutorError::Other(err) => {
RunnableAgentError::ExecutorError(err)
}
}
}
}
impl From<crate::agent::prebuilt::executor::ReActExecutorError> for RunnableAgentError {
fn from(error: crate::agent::prebuilt::executor::ReActExecutorError) -> Self {
match error {
crate::agent::prebuilt::executor::ReActExecutorError::LLMError(err) => {
RunnableAgentError::LLMError(err)
}
other => RunnableAgentError::ExecutorError(other.to_string()),
}
}
}
#[cfg(not(target_arch = "wasm32"))]
impl<T> From<tokio::sync::mpsc::error::SendError<T>> for RunnableAgentError
where
T: Debug + Send + 'static,
{
fn from(error: tokio::sync::mpsc::error::SendError<T>) -> Self {
Self::EventSendError(error.to_string())
}
}
#[derive(Debug, thiserror::Error)]
pub enum AgentBuildError {
#[error("Build Failure")]
BuildFailure(String),
#[cfg(not(target_arch = "wasm32"))]
#[error("SpawnError")]
SpawnError(#[from] SpawnErr),
}
impl AgentBuildError {
pub fn build_failure(msg: impl Into<String>) -> Self {
Self::BuildFailure(msg.into())
}
}
#[derive(Error, Debug)]
pub enum AgentResultError {
#[error("No output available in result")]
NoOutput,
#[error("Failed to deserialize executor output: {0}")]
DeserializationError(#[from] serde_json::Error),
#[error("Agent output extraction error: {0}")]
AgentOutputError(String),
}
impl AgentResultError {
pub fn agent_output_error(msg: impl Into<String>) -> Self {
Self::AgentOutputError(msg.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::agent::prebuilt::executor::{BasicExecutorError, ReActExecutorError};
use autoagents_llm::error::GuardrailPhase;
use tokio::sync::mpsc;
#[test]
fn test_runnable_agent_error_display() {
let error = RunnableAgentError::ExecutorError("Test error".to_string());
assert_eq!(error.to_string(), "Agent execution failed: Test error");
let error = RunnableAgentError::TaskError("Task failed".to_string());
assert_eq!(error.to_string(), "Task processing failed: Task failed");
let error = RunnableAgentError::AgentNotFound(uuid::Uuid::new_v4());
assert!(error.to_string().contains("Agent not found:"));
}
#[test]
fn test_runnable_agent_error_constructors() {
let error = RunnableAgentError::executor_error(std::io::Error::other("IO error"));
assert!(matches!(error, RunnableAgentError::ExecutorError(_)));
let error = RunnableAgentError::task_error("Custom task error");
assert!(matches!(error, RunnableAgentError::TaskError(_)));
let error = RunnableAgentError::event_send_error(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
"Send failed",
));
assert!(matches!(error, RunnableAgentError::EventSendError(_)));
}
#[tokio::test]
async fn test_runnable_agent_error_from_mpsc_send_error() {
let (_tx, rx) = mpsc::channel::<String>(1);
drop(rx);
let (tx, _rx) = mpsc::channel::<String>(1);
drop(tx);
let result: Result<(), mpsc::error::SendError<String>> =
Err(mpsc::error::SendError("test message".to_string()));
if let Err(send_error) = result {
let agent_error: RunnableAgentError = send_error.into();
assert!(matches!(agent_error, RunnableAgentError::EventSendError(_)));
}
}
#[test]
fn test_agent_build_error_display() {
let error = AgentBuildError::BuildFailure("Failed to build agent".to_string());
assert_eq!(error.to_string(), "Build Failure");
let error = AgentBuildError::build_failure("Custom build failure");
assert!(matches!(error, AgentBuildError::BuildFailure(_)));
}
#[test]
fn test_agent_result_error_display() {
let error = AgentResultError::NoOutput;
assert_eq!(error.to_string(), "No output available in result");
let error = AgentResultError::AgentOutputError("Custom output error".to_string());
assert_eq!(
error.to_string(),
"Agent output extraction error: Custom output error"
);
let error = AgentResultError::agent_output_error("Helper constructor error");
assert!(matches!(error, AgentResultError::AgentOutputError(_)));
}
#[test]
fn test_agent_result_error_from_json_error() {
let invalid_json = "{ invalid json }";
let json_error: Result<serde_json::Value, serde_json::Error> =
serde_json::from_str(invalid_json);
if let Err(json_err) = json_error {
let agent_error: AgentResultError = json_err.into();
assert!(matches!(
agent_error,
AgentResultError::DeserializationError(_)
));
}
}
#[test]
fn test_error_debug_formatting() {
let error = RunnableAgentError::InitializationError("Init failed".to_string());
let debug_str = format!("{error:?}");
assert!(debug_str.contains("InitializationError"));
assert!(debug_str.contains("Init failed"));
}
#[test]
fn test_from_llm_error_preserves_typed_llm_error_direct() {
let source = LLMError::GuardrailBlocked {
phase: GuardrailPhase::Input,
guard: "prompt-injection".to_string().into(),
rule_id: "prompt_injection_detected".to_string().into(),
category: "prompt_injection".to_string().into(),
severity: "high".to_string().into(),
message: "detected suspicious instruction pattern: jailbreak"
.to_string()
.into(),
};
let converted: RunnableAgentError = source.into();
assert!(matches!(
converted,
RunnableAgentError::LLMError(LLMError::GuardrailBlocked { .. })
));
}
#[test]
fn test_from_basic_executor_preserves_typed_llm_error() {
let wrapped = BasicExecutorError::from(LLMError::GuardrailBlocked {
phase: GuardrailPhase::Input,
guard: "prompt-injection".to_string().into(),
rule_id: "prompt_injection_detected".to_string().into(),
category: "prompt_injection".to_string().into(),
severity: "high".to_string().into(),
message: "detected suspicious instruction pattern: jailbreak"
.to_string()
.into(),
});
let converted: RunnableAgentError = wrapped.into();
assert!(matches!(
converted,
RunnableAgentError::LLMError(LLMError::GuardrailBlocked { .. })
));
}
#[test]
fn test_from_react_executor_preserves_typed_llm_error() {
let wrapped = ReActExecutorError::from(LLMError::GuardrailBlocked {
phase: GuardrailPhase::Input,
guard: "prompt-injection".to_string().into(),
rule_id: "prompt_injection_detected".to_string().into(),
category: "prompt_injection".to_string().into(),
severity: "high".to_string().into(),
message: "detected suspicious instruction pattern: jailbreak"
.to_string()
.into(),
});
let converted: RunnableAgentError = wrapped.into();
assert!(matches!(
converted,
RunnableAgentError::LLMError(LLMError::GuardrailBlocked { .. })
));
}
}