Skip to main content

litellm_rs/utils/error/gateway_error/
http_mapping.rs

1//! Canonical HTTP mapping for gateway/provider errors.
2
3use super::types::GatewayError;
4use crate::core::providers::unified_provider::{
5    ProviderHttpErrorFacts, provider_http_error_facts as provider_core_http_error_facts,
6};
7use actix_web::http::StatusCode;
8
9/// Optional HTTP headers carried by an error mapping.
10#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
11pub struct HttpErrorHeaders {
12    pub retry_after: Option<u64>,
13    pub rpm_limit: Option<u32>,
14    pub tpm_limit: Option<u32>,
15}
16
17/// Protocol-neutral HTTP facts shared by response adapters.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct HttpErrorFacts {
20    pub status: StatusCode,
21    pub gateway_code: &'static str,
22    pub openai_error_type: &'static str,
23    pub openai_code: &'static str,
24    pub headers: HttpErrorHeaders,
25}
26
27impl HttpErrorFacts {
28    const fn new(
29        status: StatusCode,
30        gateway_code: &'static str,
31        openai_error_type: &'static str,
32        openai_code: &'static str,
33    ) -> Self {
34        Self {
35            status,
36            gateway_code,
37            openai_error_type,
38            openai_code,
39            headers: HttpErrorHeaders {
40                retry_after: None,
41                rpm_limit: None,
42                tpm_limit: None,
43            },
44        }
45    }
46
47    const fn with_headers(mut self, headers: HttpErrorHeaders) -> Self {
48        self.headers = headers;
49        self
50    }
51}
52
53pub fn gateway_http_error_facts(error: &GatewayError) -> HttpErrorFacts {
54    match error {
55        GatewayError::Config(_) => facts(
56            StatusCode::INTERNAL_SERVER_ERROR,
57            "CONFIG_ERROR",
58            "server_error",
59            "internal_error",
60        ),
61        GatewayError::Storage(_) => facts(
62            StatusCode::SERVICE_UNAVAILABLE,
63            "STORAGE_ERROR",
64            "server_error",
65            "service_unavailable",
66        ),
67        GatewayError::Auth(_) => facts(
68            StatusCode::UNAUTHORIZED,
69            "AUTH_ERROR",
70            "authentication_error",
71            "authentication_error",
72        ),
73        GatewayError::Forbidden(_) => facts(
74            StatusCode::FORBIDDEN,
75            "FORBIDDEN",
76            "permission_error",
77            "permission_denied",
78        ),
79        GatewayError::Provider(provider_error) => {
80            provider_facts_to_http_error_facts(provider_core_http_error_facts(provider_error))
81        }
82        GatewayError::RateLimit {
83            retry_after,
84            rpm_limit,
85            tpm_limit,
86            ..
87        } => facts(
88            StatusCode::TOO_MANY_REQUESTS,
89            "RATE_LIMIT_EXCEEDED",
90            "rate_limit_error",
91            "rate_limit_exceeded",
92        )
93        .with_headers(HttpErrorHeaders {
94            retry_after: *retry_after,
95            rpm_limit: *rpm_limit,
96            tpm_limit: *tpm_limit,
97        }),
98        GatewayError::Validation(_) => facts(
99            StatusCode::BAD_REQUEST,
100            "VALIDATION_ERROR",
101            "invalid_request_error",
102            "invalid_request",
103        ),
104        GatewayError::NotFound(_) => facts(
105            StatusCode::NOT_FOUND,
106            "NOT_FOUND",
107            "invalid_request_error",
108            "not_found",
109        ),
110        GatewayError::Conflict(_) => facts(
111            StatusCode::CONFLICT,
112            "CONFLICT",
113            "invalid_request_error",
114            "conflict",
115        ),
116        GatewayError::BadRequest(_) => facts(
117            StatusCode::BAD_REQUEST,
118            "BAD_REQUEST",
119            "invalid_request_error",
120            "invalid_request",
121        ),
122        GatewayError::Timeout(_) => facts(
123            StatusCode::REQUEST_TIMEOUT,
124            "TIMEOUT",
125            "server_error",
126            "timeout",
127        ),
128        GatewayError::Unavailable(_) => facts(
129            StatusCode::SERVICE_UNAVAILABLE,
130            "SERVICE_UNAVAILABLE",
131            "server_error",
132            "service_unavailable",
133        ),
134        GatewayError::Network(_) => facts(
135            StatusCode::BAD_GATEWAY,
136            "NETWORK_ERROR",
137            "server_error",
138            "network_error",
139        ),
140        GatewayError::Internal(_) => facts(
141            StatusCode::INTERNAL_SERVER_ERROR,
142            "INTERNAL_ERROR",
143            "server_error",
144            "internal_error",
145        ),
146        GatewayError::NotImplemented(_) => facts(
147            StatusCode::NOT_IMPLEMENTED,
148            "NOT_IMPLEMENTED",
149            "invalid_request_error",
150            "not_implemented",
151        ),
152        GatewayError::Serialization(_) => facts(
153            StatusCode::BAD_REQUEST,
154            "SERIALIZATION_ERROR",
155            "invalid_request_error",
156            "invalid_request",
157        ),
158        GatewayError::HttpClient(_) => facts(
159            StatusCode::BAD_GATEWAY,
160            "HTTP_CLIENT_ERROR",
161            "server_error",
162            "network_error",
163        ),
164        GatewayError::Io(_) => facts(
165            StatusCode::INTERNAL_SERVER_ERROR,
166            "IO_ERROR",
167            "server_error",
168            "internal_error",
169        ),
170    }
171}
172
173const fn facts(
174    status: StatusCode,
175    gateway_code: &'static str,
176    openai_error_type: &'static str,
177    openai_code: &'static str,
178) -> HttpErrorFacts {
179    HttpErrorFacts::new(status, gateway_code, openai_error_type, openai_code)
180}
181
182fn provider_facts_to_http_error_facts(core_facts: ProviderHttpErrorFacts) -> HttpErrorFacts {
183    HttpErrorFacts::new(
184        status_from_u16(core_facts.status),
185        core_facts.gateway_code,
186        core_facts.openai_error_type,
187        core_facts.openai_code,
188    )
189    .with_headers(HttpErrorHeaders {
190        retry_after: core_facts.headers.retry_after,
191        rpm_limit: core_facts.headers.rpm_limit,
192        tpm_limit: core_facts.headers.tpm_limit,
193    })
194}
195
196fn status_from_u16(status: u16) -> StatusCode {
197    StatusCode::from_u16(status).unwrap_or(StatusCode::BAD_GATEWAY)
198}