Skip to main content

litellm_rs/utils/error/gateway_error/
response.rs

1//! HTTP response handling for errors
2
3use super::types::GatewayError;
4use crate::core::providers::unified_provider::ProviderError;
5use crate::utils::error::canonical::CanonicalError;
6use actix_web::{HttpResponse, ResponseError};
7
8impl ResponseError for GatewayError {
9    fn error_response(&self) -> HttpResponse {
10        let (status_code, error_code, message) = match self {
11            GatewayError::Config(_) => (
12                actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
13                "CONFIG_ERROR",
14                self.to_string(),
15            ),
16            GatewayError::Storage(_) => (
17                actix_web::http::StatusCode::SERVICE_UNAVAILABLE,
18                "STORAGE_ERROR",
19                self.to_string(),
20            ),
21            GatewayError::Auth(_) => (
22                actix_web::http::StatusCode::UNAUTHORIZED,
23                "AUTH_ERROR",
24                self.to_string(),
25            ),
26            GatewayError::Forbidden(_) => (
27                actix_web::http::StatusCode::FORBIDDEN,
28                "FORBIDDEN",
29                self.to_string(),
30            ),
31            GatewayError::Provider(provider_error) => match provider_error {
32                ProviderError::RateLimit { .. } => (
33                    actix_web::http::StatusCode::TOO_MANY_REQUESTS,
34                    "PROVIDER_RATE_LIMIT",
35                    provider_error.to_string(),
36                ),
37                ProviderError::QuotaExceeded { .. } => (
38                    actix_web::http::StatusCode::PAYMENT_REQUIRED,
39                    "PROVIDER_QUOTA_EXCEEDED",
40                    provider_error.to_string(),
41                ),
42                ProviderError::ModelNotFound { .. } => (
43                    actix_web::http::StatusCode::NOT_FOUND,
44                    "MODEL_NOT_FOUND",
45                    provider_error.to_string(),
46                ),
47                ProviderError::InvalidRequest { .. } => (
48                    actix_web::http::StatusCode::BAD_REQUEST,
49                    "INVALID_REQUEST",
50                    provider_error.to_string(),
51                ),
52                ProviderError::Timeout { .. } => (
53                    actix_web::http::StatusCode::GATEWAY_TIMEOUT,
54                    "PROVIDER_TIMEOUT",
55                    provider_error.to_string(),
56                ),
57                ProviderError::ProviderUnavailable { .. } => (
58                    actix_web::http::StatusCode::SERVICE_UNAVAILABLE,
59                    "PROVIDER_UNAVAILABLE",
60                    provider_error.to_string(),
61                ),
62                ProviderError::Authentication { .. } => (
63                    actix_web::http::StatusCode::UNAUTHORIZED,
64                    "PROVIDER_AUTH_ERROR",
65                    provider_error.to_string(),
66                ),
67                ProviderError::Network { .. } => (
68                    actix_web::http::StatusCode::BAD_GATEWAY,
69                    "PROVIDER_NETWORK_ERROR",
70                    provider_error.to_string(),
71                ),
72                ProviderError::Configuration { .. }
73                | ProviderError::Serialization { .. }
74                | ProviderError::TransformationError { .. } => (
75                    actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
76                    "PROVIDER_INTERNAL_ERROR",
77                    provider_error.to_string(),
78                ),
79                ProviderError::ContextLengthExceeded { .. }
80                | ProviderError::ContentFiltered { .. }
81                | ProviderError::TokenLimitExceeded { .. } => (
82                    actix_web::http::StatusCode::BAD_REQUEST,
83                    "PROVIDER_REQUEST_ERROR",
84                    provider_error.to_string(),
85                ),
86                ProviderError::NotSupported { .. }
87                | ProviderError::NotImplemented { .. }
88                | ProviderError::FeatureDisabled { .. } => (
89                    actix_web::http::StatusCode::NOT_IMPLEMENTED,
90                    "PROVIDER_NOT_IMPLEMENTED",
91                    provider_error.to_string(),
92                ),
93                ProviderError::DeploymentError { .. } => (
94                    actix_web::http::StatusCode::NOT_FOUND,
95                    "DEPLOYMENT_NOT_FOUND",
96                    provider_error.to_string(),
97                ),
98                ProviderError::ResponseParsing { .. } | ProviderError::Streaming { .. } => (
99                    actix_web::http::StatusCode::BAD_GATEWAY,
100                    "PROVIDER_RESPONSE_ERROR",
101                    provider_error.to_string(),
102                ),
103                ProviderError::RoutingError { .. } => (
104                    actix_web::http::StatusCode::SERVICE_UNAVAILABLE,
105                    "PROVIDER_ROUTING_ERROR",
106                    provider_error.to_string(),
107                ),
108                ProviderError::ApiError { status, .. } => (
109                    actix_web::http::StatusCode::from_u16(*status)
110                        .unwrap_or(actix_web::http::StatusCode::BAD_GATEWAY),
111                    "PROVIDER_API_ERROR",
112                    provider_error.to_string(),
113                ),
114                ProviderError::Cancelled { .. } => (
115                    actix_web::http::StatusCode::from_u16(499)
116                        .unwrap_or(actix_web::http::StatusCode::BAD_REQUEST),
117                    "PROVIDER_CANCELLED",
118                    provider_error.to_string(),
119                ),
120                ProviderError::Other { .. } => (
121                    actix_web::http::StatusCode::BAD_GATEWAY,
122                    "PROVIDER_ERROR",
123                    provider_error.to_string(),
124                ),
125            },
126            GatewayError::RateLimit { .. } => (
127                actix_web::http::StatusCode::TOO_MANY_REQUESTS,
128                "RATE_LIMIT_EXCEEDED",
129                self.to_string(),
130            ),
131            GatewayError::Validation(_) => (
132                actix_web::http::StatusCode::BAD_REQUEST,
133                "VALIDATION_ERROR",
134                self.to_string(),
135            ),
136            GatewayError::NotFound(_) => (
137                actix_web::http::StatusCode::NOT_FOUND,
138                "NOT_FOUND",
139                self.to_string(),
140            ),
141            GatewayError::Conflict(_) => (
142                actix_web::http::StatusCode::CONFLICT,
143                "CONFLICT",
144                self.to_string(),
145            ),
146            GatewayError::BadRequest(_) => (
147                actix_web::http::StatusCode::BAD_REQUEST,
148                "BAD_REQUEST",
149                self.to_string(),
150            ),
151            GatewayError::Timeout(_) => (
152                actix_web::http::StatusCode::REQUEST_TIMEOUT,
153                "TIMEOUT",
154                self.to_string(),
155            ),
156            GatewayError::Unavailable(_) => (
157                actix_web::http::StatusCode::SERVICE_UNAVAILABLE,
158                "SERVICE_UNAVAILABLE",
159                self.to_string(),
160            ),
161            GatewayError::Network(_) => (
162                actix_web::http::StatusCode::BAD_GATEWAY,
163                "NETWORK_ERROR",
164                self.to_string(),
165            ),
166            GatewayError::Internal(_) => (
167                actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
168                "INTERNAL_ERROR",
169                self.to_string(),
170            ),
171            GatewayError::NotImplemented(_) => (
172                actix_web::http::StatusCode::NOT_IMPLEMENTED,
173                "NOT_IMPLEMENTED",
174                self.to_string(),
175            ),
176            GatewayError::Serialization(_) => (
177                actix_web::http::StatusCode::BAD_REQUEST,
178                "SERIALIZATION_ERROR",
179                self.to_string(),
180            ),
181            GatewayError::HttpClient(_) => (
182                actix_web::http::StatusCode::BAD_GATEWAY,
183                "HTTP_CLIENT_ERROR",
184                self.to_string(),
185            ),
186            GatewayError::Io(_) => (
187                actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
188                "IO_ERROR",
189                self.to_string(),
190            ),
191        };
192
193        let canonical_code = self.canonical_code().as_str().to_string();
194        let retryable = self.canonical_retryable();
195
196        let error_response = GatewayErrorResponse {
197            error: GatewayErrorDetail {
198                code: error_code.to_string(),
199                canonical_code,
200                retryable,
201                message,
202                timestamp: chrono::Utc::now().timestamp(),
203                request_id: None, // This should be set by middleware
204            },
205        };
206
207        let mut builder = HttpResponse::build(status_code);
208
209        // Add rate limit headers for 429 responses
210        if let GatewayError::RateLimit {
211            retry_after,
212            rpm_limit,
213            tpm_limit,
214            ..
215        } = self
216        {
217            if let Some(secs) = retry_after {
218                builder.insert_header(("Retry-After", secs.to_string()));
219            }
220            if let Some(rpm) = rpm_limit {
221                builder.insert_header(("X-RateLimit-Limit-Requests", rpm.to_string()));
222            }
223            if let Some(tpm) = tpm_limit {
224                builder.insert_header(("X-RateLimit-Limit-Tokens", tpm.to_string()));
225            }
226        }
227
228        builder.json(error_response)
229    }
230}
231
232/// Standard gateway error response format
233#[derive(serde::Serialize)]
234pub struct GatewayErrorResponse {
235    pub error: GatewayErrorDetail,
236}
237
238/// Gateway error detail structure
239#[derive(serde::Serialize)]
240pub struct GatewayErrorDetail {
241    pub code: String,
242    pub canonical_code: String,
243    pub retryable: bool,
244    pub message: String,
245    pub timestamp: i64,
246    pub request_id: Option<String>,
247}
248
249#[cfg(test)]
250#[path = "response_tests.rs"]
251mod tests;
252
253#[cfg(test)]
254#[path = "response_consolidation_tests.rs"]
255mod consolidation_tests;