Skip to main content

bamboo_agent/server/
error.rs

1//! Server error types and HTTP response handling
2//!
3//! This module provides a unified error handling system for the Actix-web server.
4//! All errors are converted to HTTP responses with appropriate status codes.
5//!
6//! # Error Types
7//!
8//! - `BadRequest`: Client errors (400)
9//! - `ToolNotFound`: Tool not available (404)
10//! - `ToolExecutionError`: Tool execution failed (400)
11//! - `ToolApprovalRequired`: Tool needs user approval (403)
12//! - `NotFound`: Resource not found (404)
13//! - `ProxyAuthRequired`: Proxy authentication needed (428)
14//! - `InternalError`: Server errors (500)
15//! - `StorageError`: File system errors (500)
16//! - `SerializationError`: JSON serialization errors (500)
17
18use actix_web::{http::StatusCode, HttpResponse, ResponseError};
19use serde::Serialize;
20use thiserror::Error;
21
22/// Result type alias for server operations
23pub type Result<T, E = AppError> = std::result::Result<T, E>;
24
25/// Application error enum with HTTP status code mapping
26#[derive(Debug, Error)]
27pub enum AppError {
28    #[error("Bad request: {0}")]
29    BadRequest(String),
30
31    #[error("Tool '{0}' not found")]
32    ToolNotFound(String),
33
34    #[error("Tool execution failed: {0}")]
35    ToolExecutionError(String),
36
37    #[error("Tool requires approval: {0}")]
38    ToolApprovalRequired(String),
39
40    #[error("{0} not found")]
41    NotFound(String),
42
43    #[error("Proxy authentication required")]
44    ProxyAuthRequired,
45
46    #[error("Internal server error: {0}")]
47    InternalError(#[from] anyhow::Error),
48
49    #[error("Storage error: {0}")]
50    StorageError(#[from] std::io::Error),
51
52    #[error("Serialization error: {0}")]
53    SerializationError(#[from] serde_json::Error),
54}
55
56#[derive(Serialize)]
57struct JsonError {
58    message: String,
59    r#type: String,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    code: Option<String>,
62}
63
64#[derive(Serialize)]
65struct JsonErrorWrapper {
66    error: JsonError,
67}
68
69impl ResponseError for AppError {
70    fn status_code(&self) -> StatusCode {
71        match self {
72            AppError::BadRequest(_) => StatusCode::BAD_REQUEST,
73            AppError::ToolNotFound(_) => StatusCode::NOT_FOUND,
74            AppError::ToolExecutionError(_) => StatusCode::BAD_REQUEST,
75            AppError::ToolApprovalRequired(_) => StatusCode::FORBIDDEN,
76            AppError::NotFound(_) => StatusCode::NOT_FOUND,
77            AppError::ProxyAuthRequired => StatusCode::PRECONDITION_REQUIRED,
78            AppError::InternalError(_) => StatusCode::INTERNAL_SERVER_ERROR,
79            AppError::StorageError(_) => StatusCode::INTERNAL_SERVER_ERROR,
80            AppError::SerializationError(_) => StatusCode::INTERNAL_SERVER_ERROR,
81        }
82    }
83
84    fn error_response(&self) -> HttpResponse {
85        let status_code = self.status_code();
86        let error_response = JsonErrorWrapper {
87            error: JsonError {
88                message: self.to_string(),
89                r#type: "api_error".to_string(),
90                code: match self {
91                    AppError::ProxyAuthRequired => Some("proxy_auth_required".to_string()),
92                    _ => None,
93                },
94            },
95        };
96        HttpResponse::build(status_code).json(error_response)
97    }
98}