use thiserror::Error;
#[derive(Debug, Error)]
pub enum ReactError {
#[error("LLM Error: {0}")]
Llm(Box<LlmError>),
#[error("Tool Error: {0}")]
Tool(#[from] ToolError),
#[error("Parse Error: {0}")]
Parse(#[from] ParseError),
#[error("Agent Error: {0}")]
Agent(#[from] AgentError),
#[error("Config Error: {0}")]
Config(Box<ConfigError>),
#[error("MCP Error: {0}")]
Mcp(#[from] McpError),
#[error("Memory Error: {0}")]
Memory(Box<MemoryError>),
#[error("Sandbox Error: {0}")]
Sandbox(#[from] SandboxError),
#[error("Channel Error: {0}")]
Channel(#[from] ChannelError),
#[error("IO Error: {0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Other(String),
}
#[derive(Debug, Error)]
pub enum MemoryError {
#[error("IO error: {0}")]
IoError(String),
#[error("Serialization error: {0}")]
SerializationError(String),
#[error("Memory '{0}' not found")]
NotFound(String),
#[error("Unsupported operation: {0}")]
Unsupported(String),
}
impl From<std::io::Error> for MemoryError {
fn from(err: std::io::Error) -> Self {
MemoryError::IoError(err.to_string())
}
}
#[derive(Debug, Error)]
pub enum LlmError {
#[error("Network error: {0}")]
NetworkError(String),
#[error("API error (status {status}): {message}")]
ApiError {
status: u16,
message: String,
},
#[error("Invalid response: {0}")]
InvalidResponse(String),
#[error("Empty response from LLM")]
EmptyResponse,
#[error("Serialization error: {0}")]
SerializationError(String),
}
#[derive(Debug, Error)]
pub enum ToolError {
#[error("Tool '{0}' not found")]
NotFound(String),
#[error("Missing parameter: {0}")]
MissingParameter(String),
#[error("Invalid parameter '{name}': {message}")]
InvalidParameter {
name: String,
message: String,
},
#[error("Tool '{tool}' execution failed: {message}")]
ExecutionFailed {
tool: String,
message: String,
},
#[error("Tool '{0}' execution timed out")]
Timeout(String),
#[error("Invalid path: {path} ({reason})")]
InvalidPath {
path: String,
reason: String,
},
#[error("Access denied: {path} ({reason})")]
AccessDenied {
path: String,
reason: String,
},
#[error("File too large: {size} bytes (max: {max} bytes)")]
FileTooLarge {
size: u64,
max: u64,
},
}
#[derive(Debug, Error)]
pub enum ParseError {
#[error("Invalid Thought: {0}")]
InvalidThought(String),
#[error("Invalid Action: {0}")]
InvalidAction(String),
#[error("Invalid Action Input: {0}")]
InvalidActionInput(String),
#[error("JSON parse error: {0}")]
JsonError(#[from] serde_json::Error),
#[error("Unexpected format: {0}")]
UnexpectedFormat(String),
}
#[derive(Debug, Error)]
pub enum AgentError {
#[error("Max iterations exceeded: {0}")]
MaxIterationsExceeded(usize),
#[error("No tools available")]
NoToolsAvailable,
#[error("Initialization failed: {0}")]
InitializationFailed(String),
#[error("Execution interrupted")]
Interrupted,
#[error("No response from LLM (model: {model}, agent: {agent})")]
NoResponse {
model: String,
agent: String,
},
#[error("Token limit exceeded")]
TokenLimitExceeded,
#[error("Permission denied: {0}")]
PermissionDenied(String),
#[error("Hook error: {0}")]
HookError(String),
#[error("Subagent error: {0}")]
SubagentError(String),
#[error("Timeout: {0}")]
Timeout(String),
#[error("Context limit exceeded: {0}")]
ContextLimitExceeded(String),
}
#[derive(Debug, Error)]
pub enum McpError {
#[error("Connection failed: {0}")]
ConnectionFailed(String),
#[error("Initialization failed: {0}")]
InitializationFailed(String),
#[error("Protocol error: {0}")]
ProtocolError(String),
#[error("Tool call failed: {0}")]
ToolCallFailed(String),
#[error("MCP transport closed unexpectedly")]
TransportClosed,
}
#[derive(Debug, Error)]
pub enum SandboxError {
#[error("Sandbox unavailable: {0}")]
Unavailable(String),
#[error("Sandbox start failed: {0}")]
StartFailed(String),
#[error("Sandbox timeout: {0}")]
Timeout(String),
#[error("Resource exceeded: {0}")]
ResourceExceeded(String),
#[error("Permission denied: {0}")]
PermissionDenied(String),
#[error("IO error: {0}")]
IoError(String),
}
#[derive(Debug, Error)]
pub enum ChannelError {
#[error("Network error: {0}")]
NetworkError(String),
#[error("API error (status {status}): {message}")]
ApiError {
status: u16,
message: String,
},
#[error("Auth error: {0}")]
AuthError(String),
#[error("Connection error: {0}")]
ConnectionError(String),
#[error("Send error: {0}")]
SendError(String),
#[error("Invalid config: {0}")]
InvalidConfig(String),
#[error("Channel error: {0}")]
Other(String),
}
#[derive(Debug, Error)]
pub enum ConfigError {
#[error("Failed to parse environment variable: {0}")]
EnvParseError(String),
#[error("Model '{0}' missing required config: {1}")]
MissingConfig(String, String),
#[error("Invalid environment variable format: {0}")]
EnvFormatError(String),
#[error("Model '{0}' mismatched config error: {1}")]
UnMatchConfigError(String, String),
#[error("No configuration found for model: {0}")]
NotFindModelError(String),
#[error("Config file error: {0}")]
ConfigFileError(String),
}
impl From<LlmError> for ReactError {
fn from(err: LlmError) -> Self {
ReactError::Llm(Box::new(err))
}
}
impl From<ConfigError> for ReactError {
fn from(err: ConfigError) -> Self {
ReactError::Config(Box::new(err))
}
}
impl From<MemoryError> for ReactError {
fn from(err: MemoryError) -> Self {
ReactError::Memory(Box::new(err))
}
}
impl From<serde_json::Error> for ReactError {
fn from(err: serde_json::Error) -> Self {
ReactError::Parse(ParseError::JsonError(err))
}
}
#[cfg(feature = "reqwest")]
impl From<reqwest::Error> for ReactError {
fn from(err: reqwest::Error) -> Self {
if err.is_timeout() {
ReactError::Llm(Box::new(LlmError::NetworkError(
"Request timeout".to_string(),
)))
} else if err.is_connect() {
ReactError::Llm(Box::new(LlmError::NetworkError(format!(
"Connection failed: {}",
err
))))
} else {
ReactError::Llm(Box::new(LlmError::NetworkError(err.to_string())))
}
}
}
pub type Result<T> = std::result::Result<T, ReactError>;