Skip to main content

fetch_kit/
error.rs

1/// Errors that can occur when making requests with `fetch_kit`.
2#[derive(Debug, thiserror::Error)]
3pub enum FetchError {
4    /// A network-level error (DNS, connection refused, etc.).
5    #[error("network error: {0}")]
6    Network(String),
7
8    /// The request timed out.
9    #[error("request timed out after {0:?}")]
10    Timeout(std::time::Duration),
11
12    /// The server returned a non-success status code.
13    #[error("HTTP {status}: {body}")]
14    StatusCode {
15        /// HTTP status code.
16        status: u16,
17        /// Response body.
18        body: String,
19    },
20
21    /// Failed to serialize or deserialize JSON.
22    #[error("serialization error: {0}")]
23    Serialization(#[from] serde_json::Error),
24
25    /// The circuit breaker is open — requests are being rejected.
26    #[error("circuit breaker is open; requests temporarily blocked")]
27    CircuitOpen,
28
29    /// A middleware error raised inside the fetch-kit middleware chain.
30    #[error("middleware error: {0}")]
31    Middleware(String),
32
33    /// Failed to build the client.
34    #[error("build error: {0}")]
35    BuildError(String),
36
37    /// Failed to set a header.
38    #[error("header error: {0}")]
39    HeaderError(String),
40}
41
42impl From<reqwest::Error> for FetchError {
43    fn from(err: reqwest::Error) -> Self {
44        if err.is_timeout() {
45            // The reqwest::Error does not expose the configured timeout duration,
46            // so we use ZERO to indicate "the timeout triggered but the actual
47            // duration is unknown at this point."
48            FetchError::Timeout(std::time::Duration::ZERO)
49        } else {
50            FetchError::Network(err.to_string())
51        }
52    }
53}
54
55impl From<crate::middleware::Error> for FetchError {
56    fn from(err: crate::middleware::Error) -> Self {
57        match err {
58            crate::middleware::Error::Middleware(e) => {
59                // Preserve fetch_kit's own middleware errors (e.g.
60                // `CircuitOpen`, `Timeout`) so callers can match on them.
61                match e.downcast::<FetchError>() {
62                    Ok(fetch_err) => *fetch_err,
63                    Err(e) => FetchError::Middleware(e.to_string()),
64                }
65            }
66            crate::middleware::Error::Reqwest(e) => FetchError::from(e),
67        }
68    }
69}