Skip to main content

cognate_core/
error.rs

1//! Error types for Cognate.
2#![allow(missing_docs)]
3
4use thiserror::Error;
5
6/// The main error type for all Cognate operations.
7#[derive(Error, Debug)]
8pub enum Error {
9    /// An HTTP-level transport error from `reqwest`.
10    #[error("HTTP request failed: {0}")]
11    Http(#[from] reqwest::Error),
12
13    /// JSON serialisation or deserialisation failed.
14    #[error("JSON error: {0}")]
15    Json(#[from] serde_json::Error),
16
17    /// The provider API returned an error status code.
18    #[error("API error (status {status}): {message}")]
19    Api {
20        /// HTTP status code returned by the provider.
21        status: u16,
22        /// Error message from the provider's response body.
23        message: String,
24    },
25
26    /// The provider rate-limited this request.
27    #[error("Rate limit exceeded — retry after {retry_after}s")]
28    RateLimit {
29        /// Number of seconds the caller should wait before retrying.
30        retry_after: u64,
31    },
32
33    /// The request was invalid (missing required field, bad parameter, etc.).
34    #[error("Invalid request: {0}")]
35    InvalidRequest(String),
36
37    /// The provider is not configured correctly (missing API key, bad URL, etc.).
38    #[error("Provider configuration error: {0}")]
39    Configuration(String),
40
41    /// An error occurred while reading or parsing a streaming response.
42    #[error("Stream error: {0}")]
43    Stream(String),
44
45    /// The request exceeded the configured timeout.
46    #[error("Request timed out after {0}s")]
47    Timeout(u64),
48
49    /// All automatic retry attempts were exhausted.
50    #[error("Max retries ({0}) exceeded")]
51    RetryExhausted(u32),
52
53    /// A vector store operation failed.
54    #[error("Vector store error: {0}")]
55    VectorStore(String),
56}
57
58/// A convenience type alias for `Result<T, cognate_core::Error>`.
59pub type Result<T> = std::result::Result<T, Error>;
60
61impl Error {
62    /// Returns `true` if this error is a rate-limit error.
63    pub fn is_rate_limit(&self) -> bool {
64        matches!(self, Error::RateLimit { .. })
65    }
66
67    /// Returns `true` if this error is safe to retry automatically.
68    pub fn is_retryable(&self) -> bool {
69        match self {
70            Error::Http(e) => {
71                e.is_timeout()
72                    || e.is_connect()
73                    || e.status()
74                        .map(|s| s.is_server_error() || s.as_u16() == 429)
75                        .unwrap_or(false)
76            }
77            Error::RateLimit { .. } | Error::Timeout(_) => true,
78            Error::Api { status, .. } => {
79                matches!(status, 429 | 500 | 502 | 503 | 504)
80            }
81            Error::RetryExhausted(_) => false,
82            _ => false,
83        }
84    }
85
86    /// Return the recommended retry delay in seconds, if known.
87    pub fn retry_after(&self) -> Option<u64> {
88        match self {
89            Error::RateLimit { retry_after } => Some(*retry_after),
90            _ => None,
91        }
92    }
93}