pub enum ProviderErrorKind {
Transport,
Timeout,
RateLimited,
Server,
Auth,
Request,
Malformed,
}Expand description
Why a provider call failed, and therefore whether trying it again — or trying a different provider — is worth doing.
Before 0.11.0 every provider failure was one string: a 429, a 503, a 401 and
a DNS failure differed only in prose, so nothing above the provider could
branch on them. This enum is that branch, and ProviderErrorKind::is_retryable
is the decision the rest of the crate reads.
A caller branches on the kind rather than on the message, because the message wording is explicitly not part of the public contract and the kind is:
use io_harness::{Error, ProviderErrorKind};
use std::time::Duration;
// What a run that exhausted its retries hands back. The three answers a
// caller actually has are "wait this long", "try again now", and "stop".
match failure {
// The only kind that carries *when*. Honour the server's own number
// rather than backing off blind, then resume under the original run id.
Error::Provider { kind: ProviderErrorKind::RateLimited, retry_after, .. } => {
("wait, then resume", Some(retry_after.unwrap_or(Duration::from_secs(30))))
}
// Auth and Request are terminal: the key will not become valid, and the
// server has already read this exact request and refused it. Retrying
// burns the budget to reach the same failure and hides it behind a later one.
Error::Provider { kind, .. } if !kind.is_retryable() => ("stop and page a human", None),
Error::Provider { .. } => ("resume; another attempt can succeed", None),
_ => ("not a provider failure", None),
}Variants§
Transport
The request never completed: connection refused, DNS failure, TLS failure, a mid-stream byte error.
Timeout
The request had a deadline and passed it.
RateLimited
- Carries the server’s
Retry-Afterwhen it sent one.
Server
5xx.
Auth
401 or 403 — the key is wrong, missing, or not entitled to this model.
Request
4xx other than 401/403: the request itself is unacceptable.
Malformed
The response arrived and nothing in it could be read.
Implementations§
Source§impl ProviderErrorKind
impl ProviderErrorKind
Sourcepub fn is_retryable(self) -> bool
pub fn is_retryable(self) -> bool
Whether re-sending the same request could plausibly succeed.
Per arm, and the reasoning is the point — a retry that cannot work costs the budget twice and hides the real failure behind a later one:
Transport: the request never reached the model, so nothing about it was rejected. Networks drop connections for reasons that do not repeat.Timeout: the deadline says nothing about the request. A hung socket or an overloaded server is the usual cause and neither is permanent.RateLimited: the server is explicitly telling you to come back later. This is the one kind that carries when.Server: a 5xx is the server’s own admission that the fault is its side, not the request’s.Malformed: a garbled or empty stream is far more often a truncated transfer than a model that genuinely produced nothing, and re-asking is cheap — cheaper than a run that reads the emptiness as “the model chose not to call a tool” and quietly stops.Auth: the key will not become valid between two calls a second apart. Retrying burns the retry budget to reach the same 401.Request: the server has already read this exact request and refused it. Sending it again is two failures instead of one.
Sourcepub fn from_status(status: u16) -> Self
pub fn from_status(status: u16) -> Self
The kind an HTTP status maps to.
The one place that mapping lives, so OpenRouter, OpenAI and Anthropic
cannot drift apart: they were byte-identical before this existed and this
is what keeps them so. A status the buckets do not cover (1xx, 3xx — the
client follows no redirects, so a 3xx is a boundary hop, not a success)
is Request: unexpected, and not something a retry fixes.
Trait Implementations§
Source§impl Clone for ProviderErrorKind
impl Clone for ProviderErrorKind
Source§fn clone(&self) -> ProviderErrorKind
fn clone(&self) -> ProviderErrorKind
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreimpl Copy for ProviderErrorKind
Source§impl Debug for ProviderErrorKind
impl Debug for ProviderErrorKind
impl Eq for ProviderErrorKind
Source§impl PartialEq for ProviderErrorKind
impl PartialEq for ProviderErrorKind
impl StructuralPartialEq for ProviderErrorKind
Auto Trait Implementations§
impl Freeze for ProviderErrorKind
impl RefUnwindSafe for ProviderErrorKind
impl Send for ProviderErrorKind
impl Sync for ProviderErrorKind
impl Unpin for ProviderErrorKind
impl UnsafeUnpin for ProviderErrorKind
impl UnwindSafe for ProviderErrorKind
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
impl<T> Scalar for T
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.