pinecone_sdk/utils/
errors.rs1use crate::openapi::apis::{Error as OpenApiError, ResponseContent};
2use anyhow::Error as AnyhowError;
3use reqwest::{self, StatusCode};
4use thiserror::Error;
5
6#[derive(Error, Debug)]
8pub enum PineconeError {
9 #[error("Unknown response error: status: {status}, message: {message}")]
11 UnknownResponseError {
12 status: StatusCode,
14 message: String,
16 },
17
18 #[error("Action forbidden error: {source}")]
20 ActionForbiddenError {
21 source: WrappedResponseContent,
23 },
24
25 #[error("API key missing error: {message}")]
27 APIKeyMissingError {
28 message: String,
30 },
31
32 #[error("Invalid headers error: {message}")]
34 InvalidHeadersError {
35 message: String,
37 },
38
39 #[error("Timeout error: {message}")]
41 TimeoutError {
42 message: String,
44 },
45
46 #[error("Connection error: {source}")]
48 ConnectionError {
49 source: AnyhowError,
51 },
52
53 #[error("Reqwest error: {source}")]
55 ReqwestError {
56 source: AnyhowError,
58 },
59
60 #[error("Serde error: {source}")]
62 SerdeError {
63 source: AnyhowError,
65 },
66
67 #[error("IO error: {message}")]
69 IoError {
70 message: String,
72 },
73
74 #[error("Bad request error: {source}")]
76 BadRequestError {
77 source: WrappedResponseContent,
79 },
80
81 #[error("Unauthorized error: {source}")]
83 UnauthorizedError {
84 source: WrappedResponseContent,
86 },
87
88 #[error("Pod quota exceeded error: {source}")]
90 PodQuotaExceededError {
91 source: WrappedResponseContent,
93 },
94
95 #[error("Collections quota exceeded error: {source}")]
97 CollectionsQuotaExceededError {
98 source: WrappedResponseContent,
100 },
101
102 #[error("Invalid cloud error: {source}")]
104 InvalidCloudError {
105 source: WrappedResponseContent,
107 },
108
109 #[error("Invalid region error: {source}")]
111 InvalidRegionError {
112 source: WrappedResponseContent,
114 },
115
116 #[error("Invalid configuration error: {message}")]
118 InvalidConfigurationError {
119 message: String,
121 },
122
123 #[error("Collection not found error: {source}")]
125 CollectionNotFoundError {
126 source: WrappedResponseContent,
128 },
129
130 #[error("Index not found error: {source}")]
132 IndexNotFoundError {
133 source: WrappedResponseContent,
135 },
136
137 #[error("Resource already exists error: {source}")]
139 ResourceAlreadyExistsError {
140 source: WrappedResponseContent,
142 },
143
144 #[error("Unprocessable entity error: {source}")]
146 UnprocessableEntityError {
147 source: WrappedResponseContent,
149 },
150
151 #[error("Pending collection error: {source}")]
153 PendingCollectionError {
154 source: WrappedResponseContent,
156 },
157
158 #[error("Internal server error: {source}")]
160 InternalServerError {
161 source: WrappedResponseContent,
163 },
164
165 #[error("Data plane error: {status}")]
167 DataPlaneError {
168 status: tonic::Status,
170 },
171
172 #[error("Inference error: {status}")]
174 InferenceError {
175 status: tonic::Status,
177 },
178}
179
180impl<T> From<OpenApiError<T>> for PineconeError {
182 fn from(error: OpenApiError<T>) -> Self {
183 match error {
184 OpenApiError::Reqwest(inner) => PineconeError::ReqwestError {
185 source: inner.into(),
186 },
187 OpenApiError::Serde(inner) => PineconeError::SerdeError {
188 source: inner.into(),
189 },
190 OpenApiError::Io(inner) => PineconeError::IoError {
191 message: inner.to_string(),
192 },
193 OpenApiError::ResponseError(inner) => handle_response_error(inner.into()),
194 }
195 }
196}
197
198fn handle_response_error(source: WrappedResponseContent) -> PineconeError {
200 let status = source.status;
201 let message = source.content.clone();
202
203 match status {
204 StatusCode::BAD_REQUEST => PineconeError::BadRequestError { source },
205 StatusCode::UNAUTHORIZED => PineconeError::UnauthorizedError { source },
206 StatusCode::FORBIDDEN => parse_forbidden_error(source, message),
207 StatusCode::NOT_FOUND => parse_not_found_error(source, message),
208 StatusCode::CONFLICT => PineconeError::ResourceAlreadyExistsError { source },
209 StatusCode::PRECONDITION_FAILED => PineconeError::PendingCollectionError { source },
210 StatusCode::UNPROCESSABLE_ENTITY => PineconeError::UnprocessableEntityError { source },
211 StatusCode::INTERNAL_SERVER_ERROR => PineconeError::InternalServerError { source },
212 _ => PineconeError::UnknownResponseError { status, message },
213 }
214}
215
216fn parse_not_found_error(source: WrappedResponseContent, message: String) -> PineconeError {
217 if message.contains("Index") {
218 PineconeError::IndexNotFoundError { source }
219 } else if message.contains("Collection") {
220 PineconeError::CollectionNotFoundError { source }
221 } else if message.contains("region") {
222 PineconeError::InvalidRegionError { source }
223 } else if message.contains("cloud") {
224 PineconeError::InvalidCloudError { source }
225 } else {
226 PineconeError::InternalServerError { source }
227 }
228}
229
230fn parse_forbidden_error(source: WrappedResponseContent, message: String) -> PineconeError {
231 if message.contains("Deletion protection") {
232 PineconeError::ActionForbiddenError { source }
233 } else if message.contains("index") {
234 PineconeError::PodQuotaExceededError { source }
235 } else if message.contains("Collection") {
236 PineconeError::CollectionsQuotaExceededError { source }
237 } else {
238 PineconeError::InternalServerError { source }
239 }
240}
241
242#[derive(Debug)]
244pub struct WrappedResponseContent {
245 pub status: reqwest::StatusCode,
247 pub content: String,
249}
250
251impl<T> From<ResponseContent<T>> for WrappedResponseContent {
252 fn from(rc: ResponseContent<T>) -> Self {
253 WrappedResponseContent {
254 status: rc.status,
255 content: rc.content,
256 }
257 }
258}
259
260impl std::error::Error for WrappedResponseContent {
261 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
262 None
263 }
264}
265
266impl std::fmt::Display for WrappedResponseContent {
267 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
268 write!(f, "status: {} content: {}", self.status, self.content)
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::PineconeError;
275 use tokio;
276
277 fn assert_send_sync<T: Send + Sync>() {}
278
279 #[tokio::test]
280 async fn test_pinecone_error_is_send_sync() {
281 assert_send_sync::<PineconeError>();
282 }
283}