openai_ergonomic/
errors.rs1use thiserror::Error;
7
8pub type Result<T> = std::result::Result<T, Error>;
10
11#[derive(Error, Debug)]
13pub enum Error {
14 #[error("Invalid request: {0}")]
16 InvalidRequest(String),
17
18 #[error("Authentication failed: {0}")]
20 Authentication(String),
21
22 #[error("Rate limit exceeded: {0}")]
24 RateLimit(String),
25
26 #[error("HTTP error: {0}")]
28 Http(#[from] reqwest::Error),
29
30 #[error("JSON error: {0}")]
32 Json(#[from] serde_json::Error),
33
34 #[error("`OpenAI` API error (status {status}): {message}")]
36 Api {
37 status: u16,
39 message: String,
41 error_type: Option<String>,
43 error_code: Option<String>,
45 },
46
47 #[error("Stream error: {0}")]
49 Stream(String),
50
51 #[error("File error: {0}")]
53 File(#[from] std::io::Error),
54
55 #[error("Configuration error: {0}")]
57 Config(String),
58
59 #[error("Builder validation error: {0}")]
61 Builder(String),
62
63 #[error("Internal error: {0}")]
65 Internal(String),
66}
67
68impl Error {
69 pub fn api(status: u16, message: impl Into<String>) -> Self {
71 Self::Api {
72 status,
73 message: message.into(),
74 error_type: None,
75 error_code: None,
76 }
77 }
78
79 pub fn api_detailed(
81 status: u16,
82 message: impl Into<String>,
83 error_type: Option<String>,
84 error_code: Option<String>,
85 ) -> Self {
86 Self::Api {
87 status,
88 message: message.into(),
89 error_type,
90 error_code,
91 }
92 }
93
94 pub fn is_rate_limit(&self) -> bool {
96 matches!(self, Error::RateLimit(_)) || matches!(self, Error::Api { status: 429, .. })
97 }
98
99 pub fn is_auth_error(&self) -> bool {
101 matches!(self, Error::Authentication(_)) || matches!(self, Error::Api { status: 401, .. })
102 }
103
104 pub fn is_client_error(&self) -> bool {
106 match self {
107 Error::Api { status, .. } => (400..500).contains(status),
108 Error::Authentication(_) | Error::RateLimit(_) | Error::InvalidRequest(_) => true,
109 _ => false,
110 }
111 }
112
113 pub fn is_server_error(&self) -> bool {
115 match self {
116 Error::Api { status, .. } => (500..600).contains(status),
117 _ => false,
118 }
119 }
120
121 pub fn is_retryable(&self) -> bool {
123 self.is_rate_limit() || self.is_server_error()
124 }
125}
126
127pub mod chat {
129 use super::Error;
130
131 pub fn invalid_messages(msg: impl Into<String>) -> Error {
133 Error::InvalidRequest(format!("Invalid chat messages: {}", msg.into()))
134 }
135
136 pub fn unsupported_model(model: impl Into<String>) -> Error {
138 Error::InvalidRequest(format!("Unsupported model: {}", model.into()))
139 }
140}
141
142pub mod responses {
144 use super::Error;
145
146 pub fn invalid_tool(msg: impl Into<String>) -> Error {
148 Error::InvalidRequest(format!("Invalid tool definition: {}", msg.into()))
149 }
150
151 pub fn missing_response_format() -> Error {
153 Error::InvalidRequest("Response format is required for structured outputs".to_string())
154 }
155}
156
157pub mod files {
159 use super::Error;
160
161 pub fn upload_failed(msg: impl Into<String>) -> Error {
163 Error::File(std::io::Error::new(
164 std::io::ErrorKind::Other,
165 format!("File upload failed: {}", msg.into()),
166 ))
167 }
168
169 pub fn unsupported_type(file_type: impl Into<String>) -> Error {
171 Error::InvalidRequest(format!("Unsupported file type: {}", file_type.into()))
172 }
173}
174
175pub mod streaming {
177 use super::Error;
178
179 pub fn connection_failed(msg: impl Into<String>) -> Error {
181 Error::Stream(format!("Stream connection failed: {}", msg.into()))
182 }
183
184 pub fn parse_failed(msg: impl Into<String>) -> Error {
186 Error::Stream(format!("Stream parsing failed: {}", msg.into()))
187 }
188}