anthropic_async/
error.rs

1use reqwest::StatusCode;
2use serde::{Deserialize, Serialize};
3use thiserror::Error;
4
5/// Errors that can occur when using the Anthropic API client
6#[derive(Debug, Error)]
7pub enum AnthropicError {
8    /// HTTP request error
9    #[error("HTTP error: {0}")]
10    Reqwest(#[from] reqwest::Error),
11
12    /// API error returned by Anthropic
13    #[error("API error: {0:?}")]
14    Api(ApiErrorObject),
15
16    /// Configuration error (e.g., missing credentials)
17    #[error("Invalid configuration: {0}")]
18    Config(String),
19
20    /// Serialization/deserialization error
21    #[error("Serialization error: {0}")]
22    Serde(String),
23}
24
25/// API error object from Anthropic
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct ApiErrorObject {
28    /// Error type (e.g., "`invalid_request_error`", "`rate_limit_error`")
29    pub r#type: Option<String>,
30    /// Human-readable error message
31    pub message: String,
32    /// Request ID for debugging
33    pub request_id: Option<String>,
34    /// Error code
35    pub code: Option<String>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39struct ErrorEnvelope {
40    error: ApiErrorObject,
41}
42
43/// Maps a serde deserialization error to an `AnthropicError` with context
44///
45/// Includes a snippet of the response body for debugging.
46#[must_use]
47pub fn map_deser(e: &serde_json::Error, body: &[u8]) -> AnthropicError {
48    let snippet = String::from_utf8_lossy(&body[..body.len().min(400)]).to_string();
49    AnthropicError::Serde(format!("{e}: {snippet}"))
50}
51
52/// Deserializes an API error from the response body
53///
54/// Attempts to parse the error as JSON, falling back to plain text on failure.
55#[must_use]
56pub fn deserialize_api_error(status: StatusCode, body: &[u8]) -> AnthropicError {
57    if let Ok(envelope) = serde_json::from_slice::<ErrorEnvelope>(body) {
58        return AnthropicError::Api(envelope.error);
59    }
60
61    // Server may return plain text on 5xx
62    AnthropicError::Api(ApiErrorObject {
63        r#type: Some(format!("http_{}", status.as_u16())),
64        message: String::from_utf8_lossy(body).into_owned(),
65        request_id: None,
66        code: None,
67    })
68}