Skip to main content

litellm_rs/utils/error/gateway_error/
conversions.rs

1//! Type conversions for GatewayError
2
3use super::types::GatewayError;
4use crate::core::a2a::error::A2AError;
5use crate::core::a2a::message::A2AResponseError;
6use crate::core::mcp::error::McpError;
7use crate::core::mcp::protocol::JsonRpcError;
8use crate::core::providers::unified_provider::ProviderError;
9
10// Conversion from unified ProviderError to GatewayError.
11// The ResponseError impl in response.rs handles all ProviderError variants
12// via GatewayError::Provider, so no destructuring is needed here.
13impl From<ProviderError> for GatewayError {
14    fn from(err: ProviderError) -> Self {
15        GatewayError::Provider(err)
16    }
17}
18
19// Conversion from A2AError to GatewayError
20impl From<A2AError> for GatewayError {
21    fn from(err: A2AError) -> Self {
22        // Keep protocol mapping in the runtime path so canonical A2A mapping is exercised.
23        // GatewayError message text remains unchanged for backward compatibility.
24        let _protocol_error = A2AResponseError::from_a2a_error(&err);
25
26        match err {
27            A2AError::AgentNotFound { agent_name } => {
28                GatewayError::NotFound(format!("A2A agent not found: {}", agent_name))
29            }
30            A2AError::AgentAlreadyExists { agent_name } => {
31                GatewayError::Conflict(format!("A2A agent already exists: {}", agent_name))
32            }
33            A2AError::ConnectionError {
34                agent_name,
35                message,
36            } => GatewayError::Network(format!(
37                "A2A connection error to agent '{}': {}",
38                agent_name, message
39            )),
40            A2AError::AuthenticationError {
41                agent_name,
42                message,
43            } => GatewayError::Auth(format!(
44                "A2A authentication failed for agent '{}': {}",
45                agent_name, message
46            )),
47            A2AError::TaskNotFound {
48                agent_name,
49                task_id,
50            } => GatewayError::NotFound(format!(
51                "A2A task '{}' not found on agent '{}'",
52                task_id, agent_name
53            )),
54            A2AError::TaskFailed {
55                agent_name,
56                task_id,
57                message,
58            } => GatewayError::Internal(format!(
59                "A2A task '{}' failed on agent '{}': {}",
60                task_id, agent_name, message
61            )),
62            A2AError::ProtocolError { message } => {
63                GatewayError::BadRequest(format!("A2A protocol error: {}", message))
64            }
65            A2AError::InvalidRequest { message } => {
66                GatewayError::BadRequest(format!("Invalid A2A request: {}", message))
67            }
68            A2AError::Timeout {
69                agent_name,
70                timeout_ms,
71            } => GatewayError::Timeout(format!(
72                "A2A timeout waiting for agent '{}' ({}ms)",
73                agent_name, timeout_ms
74            )),
75            A2AError::ConfigurationError { message } => {
76                GatewayError::Config(format!("A2A configuration error: {}", message))
77            }
78            A2AError::SerializationError { message } => {
79                GatewayError::Validation(format!("A2A serialization error: {}", message))
80            }
81            A2AError::UnsupportedProvider { provider } => {
82                GatewayError::NotImplemented(format!("A2A provider not supported: {}", provider))
83            }
84            A2AError::RateLimitExceeded {
85                agent_name,
86                retry_after_ms,
87            } => {
88                let msg = if let Some(ms) = retry_after_ms {
89                    format!(
90                        "A2A rate limit exceeded for agent '{}', retry after {}ms",
91                        agent_name, ms
92                    )
93                } else {
94                    format!("A2A rate limit exceeded for agent '{}'", agent_name)
95                };
96                GatewayError::RateLimit {
97                    message: msg,
98                    retry_after: None,
99                    rpm_limit: None,
100                    tpm_limit: None,
101                }
102            }
103            A2AError::AgentBusy {
104                agent_name,
105                message,
106            } => GatewayError::Unavailable(format!(
107                "A2A agent '{}' is busy: {}",
108                agent_name, message
109            )),
110            A2AError::ContentBlocked { agent_name, reason } => GatewayError::BadRequest(format!(
111                "A2A content blocked by agent '{}': {}",
112                agent_name, reason
113            )),
114        }
115    }
116}
117
118// Conversion from McpError to GatewayError
119impl From<McpError> for GatewayError {
120    fn from(err: McpError) -> Self {
121        // Keep protocol mapping in the runtime path so canonical MCP mapping is exercised.
122        // GatewayError message text remains unchanged for backward compatibility.
123        let _protocol_error = JsonRpcError::from_mcp_error(&err);
124
125        match err {
126            McpError::ServerNotFound { server_name } => {
127                GatewayError::NotFound(format!("MCP server not found: {}", server_name))
128            }
129            McpError::ToolNotFound {
130                server_name,
131                tool_name,
132            } => GatewayError::NotFound(format!(
133                "MCP tool '{}' not found on server '{}'",
134                tool_name, server_name
135            )),
136            McpError::ConnectionError {
137                server_name,
138                message,
139            } => GatewayError::Network(format!(
140                "MCP connection error to server '{}': {}",
141                server_name, message
142            )),
143            McpError::TransportError { transport, message } => {
144                GatewayError::Network(format!("MCP transport error ({}): {}", transport, message))
145            }
146            McpError::AuthenticationError {
147                server_name,
148                message,
149            } => GatewayError::Auth(format!(
150                "MCP authentication failed for server '{}': {}",
151                server_name, message
152            )),
153            McpError::AuthorizationError {
154                server_name,
155                tool_name,
156                message,
157            } => {
158                let msg = if let Some(tool) = tool_name {
159                    format!(
160                        "MCP permission denied for tool '{}' on server '{}': {}",
161                        tool, server_name, message
162                    )
163                } else {
164                    format!(
165                        "MCP permission denied for server '{}': {}",
166                        server_name, message
167                    )
168                };
169                GatewayError::Forbidden(msg)
170            }
171            McpError::ProtocolError { message } => {
172                GatewayError::BadRequest(format!("MCP protocol error: {}", message))
173            }
174            McpError::ToolExecutionError {
175                server_name,
176                tool_name,
177                code,
178                message,
179            } => GatewayError::Internal(format!(
180                "MCP tool execution failed: server='{}', tool='{}', code={}, message='{}'",
181                server_name, tool_name, code, message
182            )),
183            McpError::Timeout {
184                server_name,
185                timeout_ms,
186            } => GatewayError::Timeout(format!(
187                "MCP timeout waiting for server '{}' ({}ms)",
188                server_name, timeout_ms
189            )),
190            McpError::ConfigurationError { message } => {
191                GatewayError::Config(format!("MCP configuration error: {}", message))
192            }
193            McpError::SerializationError { message } => {
194                GatewayError::Validation(format!("MCP serialization error: {}", message))
195            }
196            McpError::ServerAlreadyExists { server_name } => {
197                GatewayError::Conflict(format!("MCP server already registered: {}", server_name))
198            }
199            McpError::InvalidUrl { url, message } => {
200                GatewayError::BadRequest(format!("Invalid MCP server URL '{}': {}", url, message))
201            }
202            McpError::RateLimitExceeded {
203                server_name,
204                retry_after_ms,
205            } => {
206                let msg = if let Some(ms) = retry_after_ms {
207                    format!(
208                        "MCP rate limit exceeded for server '{}', retry after {}ms",
209                        server_name, ms
210                    )
211                } else {
212                    format!("MCP rate limit exceeded for server '{}'", server_name)
213                };
214                GatewayError::RateLimit {
215                    message: msg,
216                    retry_after: None,
217                    rpm_limit: None,
218                    tpm_limit: None,
219                }
220            }
221            McpError::ValidationError {
222                server_name,
223                tool_name,
224                errors,
225            } => GatewayError::Validation(format!(
226                "Validation failed for tool '{}' on server '{}': {}",
227                tool_name,
228                server_name,
229                errors.join("; ")
230            )),
231        }
232    }
233}
234
235#[cfg(test)]
236#[path = "conversions_tests.rs"]
237mod tests;