1use std::{collections::BTreeMap, error::Error, fmt};
2
3#[derive(Debug)]
5pub struct OpenAIError {
6 pub kind: ErrorKind,
8 pub message: String,
10 response: Option<Box<ErrorResponseContext>>,
11 source: Option<Box<dyn Error + Send + Sync>>,
12}
13
14impl Clone for OpenAIError {
15 fn clone(&self) -> Self {
16 Self {
17 kind: self.kind.clone(),
18 message: self.message.clone(),
19 response: self.response.clone(),
20 source: None,
21 }
22 }
23}
24
25impl OpenAIError {
26 pub fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
28 Self {
29 kind,
30 message: message.into(),
31 response: None,
32 source: None,
33 }
34 }
35
36 pub fn with_response_metadata(
38 mut self,
39 status_code: u16,
40 headers: BTreeMap<String, String>,
41 request_id: Option<String>,
42 ) -> Self {
43 let mut response = self
44 .response
45 .take()
46 .unwrap_or_else(|| Box::new(ErrorResponseContext::default()));
47 response.status_code = Some(status_code);
48 response.headers = headers;
49 response.request_id = request_id;
50 self.response = Some(response);
51 self
52 }
53
54 pub fn with_api_error(mut self, api_error: ApiErrorPayload) -> Self {
56 let mut response = self
57 .response
58 .take()
59 .unwrap_or_else(|| Box::new(ErrorResponseContext::default()));
60 response.api_error = Some(api_error);
61 self.response = Some(response);
62 self
63 }
64
65 pub fn with_source<E>(mut self, source: E) -> Self
67 where
68 E: Error + Send + Sync + 'static,
69 {
70 self.source = Some(Box::new(source));
71 self
72 }
73
74 pub fn status_code(&self) -> Option<u16> {
76 self.response
77 .as_ref()
78 .and_then(|response| response.status_code)
79 }
80
81 pub fn request_id(&self) -> Option<&str> {
83 self.response
84 .as_ref()
85 .and_then(|response| response.request_id.as_deref())
86 }
87
88 pub fn header(&self, name: &str) -> Option<&str> {
90 self.response
91 .as_ref()
92 .and_then(|response| response.headers.get(&name.to_ascii_lowercase()))
93 .map(String::as_str)
94 }
95
96 pub fn api_error(&self) -> Option<&ApiErrorPayload> {
98 self.response
99 .as_ref()
100 .and_then(|response| response.api_error.as_ref())
101 }
102
103 pub fn source(&self) -> Option<&(dyn Error + 'static)> {
105 self.source
106 .as_deref()
107 .map(|source| source as &(dyn Error + 'static))
108 }
109}
110
111#[derive(Clone, Debug, Default)]
112struct ErrorResponseContext {
113 status_code: Option<u16>,
114 headers: BTreeMap<String, String>,
115 request_id: Option<String>,
116 api_error: Option<ApiErrorPayload>,
117}
118
119impl fmt::Display for OpenAIError {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 write!(f, "{}", self.message)
122 }
123}
124
125impl Error for OpenAIError {
126 fn source(&self) -> Option<&(dyn Error + 'static)> {
127 self.source()
128 }
129}
130
131#[derive(Clone, Debug, Eq, PartialEq)]
133pub enum ErrorKind {
134 Configuration,
136 Validation,
138 WebhookSignature,
140 Transport,
142 Api(ApiErrorKind),
144 Parse,
146 Timeout,
148}
149
150#[derive(Clone, Copy, Debug, Eq, PartialEq)]
152pub enum ApiErrorKind {
153 BadRequest,
154 Authentication,
155 PermissionDenied,
156 NotFound,
157 Conflict,
158 UnprocessableEntity,
159 RateLimit,
160 Server,
161 Other(u16),
162}
163
164#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize)]
166pub struct ApiErrorPayload {
167 pub message: String,
168 #[serde(rename = "type")]
169 pub error_type: Option<String>,
170 pub code: Option<String>,
171 pub param: Option<String>,
172}