Why a question went unanswered. Every variant that saw a response carries its request id, the only handle for a billing dispute.
The request would break an API rule; nothing was sent.
13 Invalid(ProtocolError),
The API refused, after any retries. The request id is inside.
15 Api { error: ApiError, attempts: u32 },
No usable response. request_id is the last one any earlier
attempt received.
18 Transport { error: TransportError, attempts: u32, request_id: Option<String> },
Every attempt ran out of time, or the budget did.
20 TimedOut { attempts: u32, request_id: Option<String> },
A 200 that does not answer the questions, or answers from another model.
26impl ClientError {
The request id to quote, if any response carried one.
28 pub fn request_id(&self) -> Option<&str> { 29 match self { 30 ClientError::Invalid(_) => None, 31 ClientError::Api { error, .. } => error.request_id.as_deref(), 32 ClientError::Transport { request_id, .. } 33 | ClientError::TimedOut { request_id, .. } 34 | ClientError::Response { request_id, .. } => request_id.as_deref(), 35 } 36 } 37}
39impl fmt::Display for ClientError { 40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 41 let attempts = |n: &u32| if *n == 1 { String::new() } else { format!(" after {n} attempts") }; 42 match self { 43 ClientError::Invalid(e) => write!(f, "{e}"), 44 // ApiError's own Display carries the request id. 45 ClientError::Api { error, attempts: n } => write!(f, "{error}{}", attempts(n)), 46 ClientError::Transport { error, attempts: n, .. } => write!(f, "{error}{}", attempts(n)), 47 ClientError::TimedOut { attempts: n, .. } => write!(f, "Jev did not answer in time{}", attempts(n)), 48 ClientError::Response { error, .. } => write!(f, "{error}"), 49 }?; 50 match self { 51 ClientError::Transport { request_id: Some(id), .. } 52 | ClientError::TimedOut { request_id: Some(id), .. } 53 | ClientError::Response { request_id: Some(id), .. } => write!(f, " [request {id}]"), 54 _ => Ok(()), 55 } 56 } 57} 58 59impl std::error::Error for ClientError {}