Skip to main content

adk_anthropic/
error.rs

1//! Error types for the adk-anthropic client.
2//!
3//! This module defines a comprehensive error type system for handling
4//! all possible errors that can occur when interacting with the Anthropic API.
5
6use std::error;
7use std::fmt;
8use std::io;
9use std::str::Utf8Error;
10use std::sync::Arc;
11
12/// The main error type for the adk-anthropic client.
13#[derive(Clone, Debug)]
14pub enum Error {
15    /// A generic API error occurred.
16    Api {
17        /// HTTP status code.
18        status_code: u16,
19        /// Error type string from the API.
20        error_type: Option<String>,
21        /// Human-readable error message.
22        message: String,
23        /// Request ID for debugging and support.
24        request_id: Option<String>,
25    },
26
27    /// Authentication error.
28    Authentication {
29        /// Human-readable error message.
30        message: String,
31    },
32
33    /// Authorization/Permission error.
34    Permission {
35        /// Human-readable error message.
36        message: String,
37    },
38
39    /// Resource not found.
40    NotFound {
41        /// Human-readable error message.
42        message: String,
43        /// Resource type.
44        resource_type: Option<String>,
45        /// Resource ID.
46        resource_id: Option<String>,
47    },
48
49    /// Rate limit exceeded.
50    RateLimit {
51        /// Human-readable error message.
52        message: String,
53        /// Time to wait before retrying, in seconds.
54        retry_after: Option<u64>,
55    },
56
57    /// Bad request due to invalid parameters.
58    BadRequest {
59        /// Human-readable error message.
60        message: String,
61        /// Parameter that caused the error.
62        param: Option<String>,
63    },
64
65    /// API timeout error.
66    Timeout {
67        /// Human-readable error message.
68        message: String,
69        /// Duration of the timeout in seconds.
70        duration: Option<f64>,
71    },
72
73    /// Request was aborted by the client.
74    Abort {
75        /// Human-readable error message.
76        message: String,
77    },
78
79    /// Connection error.
80    Connection {
81        /// Human-readable error message.
82        message: String,
83        /// Underlying cause.
84        source: Option<Arc<dyn error::Error + Send + Sync>>,
85    },
86
87    /// Server returned a 500 internal error.
88    InternalServer {
89        /// Human-readable error message.
90        message: String,
91        /// Request ID for debugging and support.
92        request_id: Option<String>,
93    },
94
95    /// Server is overloaded or unavailable.
96    ServiceUnavailable {
97        /// Human-readable error message.
98        message: String,
99        /// Time to wait before retrying, in seconds.
100        retry_after: Option<u64>,
101    },
102
103    /// Error during JSON serialization or deserialization.
104    Serialization {
105        /// Human-readable error message.
106        message: String,
107        /// The underlying error.
108        source: Option<Arc<dyn error::Error + Send + Sync>>,
109    },
110
111    /// I/O error.
112    Io {
113        /// Human-readable error message.
114        message: String,
115        /// The underlying error.
116        source: Arc<io::Error>,
117    },
118
119    /// HTTP client error.
120    HttpClient {
121        /// Human-readable error message.
122        message: String,
123        /// The underlying error.
124        source: Option<Arc<dyn error::Error + Send + Sync>>,
125    },
126
127    /// Error during validation of request parameters.
128    Validation {
129        /// Human-readable error message.
130        message: String,
131        /// Parameter that failed validation.
132        param: Option<String>,
133    },
134
135    /// A URL parsing or manipulation error.
136    Url {
137        /// Human-readable error message.
138        message: String,
139        /// The underlying error.
140        source: Option<url::ParseError>,
141    },
142
143    /// A streaming error occurred.
144    Streaming {
145        /// Human-readable error message.
146        message: String,
147        /// The underlying error.
148        source: Option<Arc<dyn error::Error + Send + Sync>>,
149    },
150
151    /// Encoding/decoding error.
152    Encoding {
153        /// Human-readable error message.
154        message: String,
155        /// The underlying error.
156        source: Option<Arc<dyn error::Error + Send + Sync>>,
157    },
158
159    /// Unknown error.
160    Unknown {
161        /// Human-readable error message.
162        message: String,
163    },
164
165    /// Unimplemented functionality.
166    ToDo {
167        /// Human-readable error message.
168        message: String,
169    },
170}
171
172impl Error {
173    /// Creates a new API error.
174    pub fn api(
175        status_code: u16,
176        error_type: Option<String>,
177        message: String,
178        request_id: Option<String>,
179    ) -> Self {
180        Error::Api { status_code, error_type, message, request_id }
181    }
182
183    /// Creates a new authentication error.
184    pub fn authentication(message: impl Into<String>) -> Self {
185        Error::Authentication { message: message.into() }
186    }
187
188    /// Creates a new permission error.
189    pub fn permission(message: impl Into<String>) -> Self {
190        Error::Permission { message: message.into() }
191    }
192
193    /// Creates a new not found error.
194    pub fn not_found(
195        message: impl Into<String>,
196        resource_type: Option<String>,
197        resource_id: Option<String>,
198    ) -> Self {
199        Error::NotFound { message: message.into(), resource_type, resource_id }
200    }
201
202    /// Creates a new rate limit error.
203    pub fn rate_limit(message: impl Into<String>, retry_after: Option<u64>) -> Self {
204        Error::RateLimit { message: message.into(), retry_after }
205    }
206
207    /// Creates a new bad request error.
208    pub fn bad_request(message: impl Into<String>, param: Option<String>) -> Self {
209        Error::BadRequest { message: message.into(), param }
210    }
211
212    /// Creates a new timeout error.
213    pub fn timeout(message: impl Into<String>, duration: Option<f64>) -> Self {
214        Error::Timeout { message: message.into(), duration }
215    }
216
217    /// Creates a new abort error.
218    pub fn abort(message: impl Into<String>) -> Self {
219        Error::Abort { message: message.into() }
220    }
221
222    /// Creates a new connection error.
223    pub fn connection(
224        message: impl Into<String>,
225        source: Option<Box<dyn error::Error + Send + Sync>>,
226    ) -> Self {
227        Error::Connection { message: message.into(), source: source.map(Arc::from) }
228    }
229
230    /// Creates a new internal server error.
231    pub fn internal_server(message: impl Into<String>, request_id: Option<String>) -> Self {
232        Error::InternalServer { message: message.into(), request_id }
233    }
234
235    /// Creates a new service unavailable error.
236    pub fn service_unavailable(message: impl Into<String>, retry_after: Option<u64>) -> Self {
237        Error::ServiceUnavailable { message: message.into(), retry_after }
238    }
239
240    /// Creates a new serialization error.
241    pub fn serialization(
242        message: impl Into<String>,
243        source: Option<Box<dyn error::Error + Send + Sync>>,
244    ) -> Self {
245        Error::Serialization { message: message.into(), source: source.map(Arc::from) }
246    }
247
248    /// Creates a new I/O error.
249    pub fn io(message: impl Into<String>, source: io::Error) -> Self {
250        Error::Io { message: message.into(), source: Arc::new(source) }
251    }
252
253    /// Creates a new HTTP client error.
254    pub fn http_client(
255        message: impl Into<String>,
256        source: Option<Box<dyn error::Error + Send + Sync>>,
257    ) -> Self {
258        Error::HttpClient { message: message.into(), source: source.map(Arc::from) }
259    }
260
261    /// Creates a new validation error.
262    pub fn validation(message: impl Into<String>, param: Option<String>) -> Self {
263        Error::Validation { message: message.into(), param }
264    }
265
266    /// Creates a new URL error.
267    pub fn url(message: impl Into<String>, source: Option<url::ParseError>) -> Self {
268        Error::Url { message: message.into(), source }
269    }
270
271    /// Creates a new streaming error.
272    pub fn streaming(
273        message: impl Into<String>,
274        source: Option<Box<dyn error::Error + Send + Sync>>,
275    ) -> Self {
276        Error::Streaming { message: message.into(), source: source.map(Arc::from) }
277    }
278
279    /// Creates a new encoding error.
280    pub fn encoding(
281        message: impl Into<String>,
282        source: Option<Box<dyn error::Error + Send + Sync>>,
283    ) -> Self {
284        Error::Encoding { message: message.into(), source: source.map(Arc::from) }
285    }
286
287    /// Creates a new unknown error.
288    pub fn unknown(message: impl Into<String>) -> Self {
289        Error::Unknown { message: message.into() }
290    }
291
292    /// Creates a new ToDo error for unimplemented functionality.
293    pub fn todo(message: impl Into<String>) -> Self {
294        Error::ToDo { message: message.into() }
295    }
296
297    /// Returns true if this error is related to authentication.
298    pub fn is_authentication(&self) -> bool {
299        matches!(self, Error::Authentication { .. })
300    }
301
302    /// Returns true if this error is related to permissions.
303    pub fn is_permission(&self) -> bool {
304        matches!(self, Error::Permission { .. })
305    }
306
307    /// Returns true if this error is a "not found" error.
308    pub fn is_not_found(&self) -> bool {
309        matches!(self, Error::NotFound { .. })
310    }
311
312    /// Returns true if this error is related to rate limiting.
313    pub fn is_rate_limit(&self) -> bool {
314        matches!(self, Error::RateLimit { .. })
315    }
316
317    /// Returns true if this error is a bad request.
318    pub fn is_bad_request(&self) -> bool {
319        matches!(self, Error::BadRequest { .. })
320    }
321
322    /// Returns true if this error is a timeout.
323    pub fn is_timeout(&self) -> bool {
324        matches!(self, Error::Timeout { .. })
325    }
326
327    /// Returns true if this error is an abort.
328    pub fn is_abort(&self) -> bool {
329        matches!(self, Error::Abort { .. })
330    }
331
332    /// Returns true if this error is a connection error.
333    pub fn is_connection(&self) -> bool {
334        matches!(self, Error::Connection { .. })
335    }
336
337    /// Returns true if this error is a server error.
338    pub fn is_server_error(&self) -> bool {
339        matches!(self, Error::InternalServer { .. } | Error::ServiceUnavailable { .. })
340    }
341
342    /// Returns true if this error is retryable.
343    pub fn is_retryable(&self) -> bool {
344        match self {
345            Error::Api { status_code, .. } => {
346                matches!(status_code, 408 | 409 | 429 | 500..=599)
347            }
348            Error::Timeout { .. } => true,
349            Error::Connection { .. } => true,
350            Error::RateLimit { .. } => true,
351            Error::ServiceUnavailable { .. } => true,
352            Error::InternalServer { .. } => true,
353            _ => false,
354        }
355    }
356
357    /// Returns true if this error is a ToDo error.
358    pub fn is_todo(&self) -> bool {
359        matches!(self, Error::ToDo { .. })
360    }
361
362    /// Returns true if this error is a validation error.
363    pub fn is_validation(&self) -> bool {
364        matches!(self, Error::Validation { .. })
365    }
366
367    /// Returns the request ID associated with this error, if any.
368    pub fn request_id(&self) -> Option<&str> {
369        match self {
370            Error::Api { request_id, .. } => request_id.as_deref(),
371            Error::InternalServer { request_id, .. } => request_id.as_deref(),
372            _ => None,
373        }
374    }
375
376    /// Returns the status code associated with this error, if any.
377    pub fn status_code(&self) -> Option<u16> {
378        match self {
379            Error::Api { status_code, .. } => Some(*status_code),
380            _ => None,
381        }
382    }
383}
384
385impl fmt::Display for Error {
386    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387        match self {
388            Error::Api { message, error_type, request_id, .. } => {
389                if let Some(error_type) = error_type {
390                    if let Some(request_id) = request_id {
391                        write!(f, "{error_type}: {message} (Request ID: {request_id})")
392                    } else {
393                        write!(f, "{error_type}: {message}")
394                    }
395                } else if let Some(request_id) = request_id {
396                    write!(f, "API error: {message} (Request ID: {request_id})")
397                } else {
398                    write!(f, "API error: {message}")
399                }
400            }
401            Error::Authentication { message } => {
402                write!(f, "Authentication error: {message}")
403            }
404            Error::Permission { message } => {
405                write!(f, "Permission error: {message}")
406            }
407            Error::NotFound { message, resource_type, resource_id } => {
408                let prefix = if let Some(resource_type) = resource_type {
409                    format!("Resource not found ({resource_type})")
410                } else {
411                    "Resource not found".to_string()
412                };
413
414                let suffix = if let Some(resource_id) = resource_id {
415                    format!(" [ID: {resource_id}]")
416                } else {
417                    "".to_string()
418                };
419
420                write!(f, "{prefix}: {message}{suffix}")
421            }
422            Error::RateLimit { message, retry_after } => {
423                if let Some(retry_after) = retry_after {
424                    write!(f, "Rate limit exceeded: {message} (retry after {retry_after} seconds)")
425                } else {
426                    write!(f, "Rate limit exceeded: {message}")
427                }
428            }
429            Error::BadRequest { message, param } => {
430                if let Some(param) = param {
431                    write!(f, "Bad request: {message} (parameter: {param})")
432                } else {
433                    write!(f, "Bad request: {message}")
434                }
435            }
436            Error::Timeout { message, duration } => {
437                if let Some(duration) = duration {
438                    write!(f, "Timeout error: {message} ({duration} seconds)")
439                } else {
440                    write!(f, "Timeout error: {message}")
441                }
442            }
443            Error::Abort { message } => {
444                write!(f, "Request aborted: {message}")
445            }
446            Error::Connection { message, .. } => {
447                write!(f, "Connection error: {message}")
448            }
449            Error::InternalServer { message, request_id } => {
450                if let Some(request_id) = request_id {
451                    write!(f, "Internal server error: {message} (Request ID: {request_id})")
452                } else {
453                    write!(f, "Internal server error: {message}")
454                }
455            }
456            Error::ServiceUnavailable { message, retry_after } => {
457                if let Some(retry_after) = retry_after {
458                    write!(f, "Service unavailable: {message} (retry after {retry_after} seconds)")
459                } else {
460                    write!(f, "Service unavailable: {message}")
461                }
462            }
463            Error::Serialization { message, .. } => {
464                write!(f, "Serialization error: {message}")
465            }
466            Error::Io { message, .. } => {
467                write!(f, "I/O error: {message}")
468            }
469            Error::HttpClient { message, .. } => {
470                write!(f, "HTTP client error: {message}")
471            }
472            Error::Validation { message, param } => {
473                if let Some(param) = param {
474                    write!(f, "Validation error: {message} (parameter: {param})")
475                } else {
476                    write!(f, "Validation error: {message}")
477                }
478            }
479            Error::Url { message, .. } => {
480                write!(f, "URL error: {message}")
481            }
482            Error::Streaming { message, .. } => {
483                write!(f, "Streaming error: {message}")
484            }
485            Error::Encoding { message, .. } => {
486                write!(f, "Encoding error: {message}")
487            }
488            Error::Unknown { message } => {
489                write!(f, "Unknown error: {message}")
490            }
491            Error::ToDo { message } => {
492                write!(f, "Unimplemented: {message}")
493            }
494        }
495    }
496}
497
498impl error::Error for Error {
499    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
500        match self {
501            Error::Connection { source, .. } => {
502                source.as_ref().map(|e| e.as_ref() as &(dyn error::Error + 'static))
503            }
504            Error::Serialization { source, .. } => {
505                source.as_ref().map(|e| e.as_ref() as &(dyn error::Error + 'static))
506            }
507            Error::Io { source, .. } => Some(source),
508            Error::HttpClient { source, .. } => {
509                source.as_ref().map(|e| e.as_ref() as &(dyn error::Error + 'static))
510            }
511            Error::Url { source, .. } => {
512                source.as_ref().map(|e| e as &(dyn error::Error + 'static))
513            }
514            Error::Streaming { source, .. } => {
515                source.as_ref().map(|e| e.as_ref() as &(dyn error::Error + 'static))
516            }
517            Error::Encoding { source, .. } => {
518                source.as_ref().map(|e| e.as_ref() as &(dyn error::Error + 'static))
519            }
520            _ => None,
521        }
522    }
523}
524
525impl From<io::Error> for Error {
526    fn from(err: io::Error) -> Self {
527        Error::io(err.to_string(), err)
528    }
529}
530
531impl From<serde_json::Error> for Error {
532    fn from(err: serde_json::Error) -> Self {
533        Error::serialization(format!("JSON error: {err}"), Some(Box::new(err)))
534    }
535}
536
537impl From<url::ParseError> for Error {
538    fn from(err: url::ParseError) -> Self {
539        Error::url(format!("URL parse error: {err}"), Some(err))
540    }
541}
542
543impl From<Utf8Error> for Error {
544    fn from(err: Utf8Error) -> Self {
545        Error::encoding(format!("UTF-8 error: {err}"), Some(Box::new(err)))
546    }
547}
548
549/// A specialized Result type for adk-anthropic operations.
550pub type Result<T> = std::result::Result<T, Error>;