Skip to main content

HttpClientError

Enum HttpClientError 

Source
#[non_exhaustive]
pub enum HttpClientError {
Show 14 variants Io { method: &'static str, error: Error, }, Refused { method: &'static str, status: StatusCode, content_type: Option<String>, preview: String, }, MediaType { got: String, }, MissingMediaType, NotAMessage { got: String, }, Unanswered { method: &'static str, }, Authorization(String), Body { method: &'static str, what: &'static str, detail: String, }, Frame { source: SseError, }, StreamEnded { delivered: u64, }, ResumeExhausted { attempts: u32, delivered: u64, source: Box<Self>, }, NoSessionYet, SessionId { source: McpSessionIdError, }, Url { got: String, reason: String, },
}
Expand description

Why a Streamable HTTP call did not produce a response the caller can use.

§Examples

Every variant is distinguishable, which is the reason this transport maps into one enum instead of letting a reqwest::Error through:

use protocol_droid::adapters::secondary::transport::http::HttpClientError;

// A refusal and a dead socket are different problems for a retry policy:
// one is the peer's answer, the other is a network worth waiting on. Both
// are `Err`, and a caller that treats `Err` as "retry" cannot tell them
// apart without matching on the variant.
let error = HttpClientError::MediaType { got: "text/html".into() };
assert!(matches!(error, HttpClientError::MediaType { .. }));
assert!(!matches!(error, HttpClientError::Io { .. }));

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

Io

The request never reached the peer, or the response never came back.

The only variant a caller should retry without thinking — and even then only for an idempotent request. A POST that may have been received and acted on before the socket failed is exactly the case the idempotency key exists for, and retrying it blind is how a tool runs twice.

Fields

§method: &'static str

The HTTP method that was in flight, so a log line names what was lost.

§error: Error

The transport’s own account of the failure.

§

Refused

The peer answered with a status outside 2xx.

Fields

§method: &'static str

The HTTP method that was refused.

§status: StatusCode

The status the peer answered with.

§content_type: Option<String>

The Content-Type, absent when the peer sent none.

§preview: String

The first REFUSAL_PREVIEW characters of the body, decoded lossily.

§

MediaType

The peer answered 2xx with a body this client cannot read.

Kept apart from HttpClientError::Refused on purpose. A 200 with the wrong content type is the peer and the client disagreeing about what a response is, and no retry fixes that — a policy that treated it as a transport blip would loop against a server that answers identically every time.

Fields

§got: String

The Content-Type the peer answered with, verbatim.

§

MissingMediaType

The peer answered 2xx with no Content-Type at all.

§

NotAMessage

The peer answered 2xx with a body that is not a JSON object.

Its own variant, not HttpClientError::MediaType: that one means “not JSON or not an event stream” and leaves the caller to pick which. Here the peer committed to JSON and then sent something that is not a JSON-RPC message, which is a different fault with a different fix.

Fields

§got: String

What came back, truncated to something loggable.

§

Unanswered

The peer’s stream closed without the answer this request was waiting on.

Its own variant because a stream that ends is a different fault from one that never began: the first means the peer accepted the request and stopped talking, and a bridge reporting it as a transport error tells the client something true about a request the server did receive.

Fields

§method: &'static str

The request that was left unanswered.

§

Authorization(String)

The server demanded authorization and the client could not provide it.

Carries the authorizer’s own account, which never contains the token.

§

Body

A body could not be encoded for sending, or a reply body could not be read back as JSON.

Separate from HttpClientError::Io because that one carries a reqwest::Error and means the request never reached the peer. This is the opposite: the bytes arrived and could not be understood. Folding both into Io would report a marshalling fault as a network fault.

Fields

§method: &'static str

The HTTP method that was in flight.

§what: &'static str

What was being attempted, in a form that reads as a verb.

§detail: String

The decoder’s own account.

§

Frame

The event stream produced bytes that are not a valid SSE frame.

Fields

§source: SseError

The frame decoder’s account of what was wrong.

§

StreamEnded

The event stream ended, or the socket broke, mid-frame.

Not terminal on its own: a resumable read treats this as “reconnect from the last delivered id” and only surfaces it once the reconnect budget is gone. See HttpClientError::ResumeExhausted.

Fields

§delivered: u64

How many events the reader had actually delivered. This is the number that decides what a resume should ask for, and getting it wrong is how a resume either repeats an event or skips one.

§

ResumeExhausted

A resumable read ran out of reconnects.

Fields

§attempts: u32

How many reconnects were attempted.

§delivered: u64

Events delivered before the budget ran out.

§source: Box<Self>

The failure that ended the last attempt.

§

NoSessionYet

A call that requires a session was made before one had been issued.

§

SessionId

The peer sent a Mcp-Session-Id this client cannot parse.

Refused rather than ignored: a client that silently drops an unparseable session id and carries on sends its next request with no session header, and the resulting failure points at the session layer instead of at the header that was actually wrong.

Fields

§source: McpSessionIdError

What was wrong with it.

§

Url

The endpoint is not a URL a request can be built against.

Fields

§got: String

The endpoint as given.

§reason: String

The URL parser’s account, rendered.

Implementations§

Source§

impl HttpClientError

Source

pub const fn is_transient(&self) -> bool

Whether retrying the identical request could plausibly succeed.

A convenience, not a policy: it says what the error implies, and the caller’s idempotency story still decides whether a retry is safe. A POST that may already have run is false even when the socket died, because the honest answer there is “ask the idempotency store”, not “send it again”.

Source

pub const fn status(&self) -> Option<StatusCode>

The status the peer answered with, when there was one.

None for every variant where the request never got an answer, which is the distinction a caller’s logging actually needs.

Trait Implementations§

Source§

impl Debug for HttpClientError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for HttpClientError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for HttpClientError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0:

use the Display impl or to_string()

1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0:

replaced by Error::source, which can support downcasting

Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<FrameError> for HttpClientError

Maps a frame failure onto the one enum a caller above this layer matches.

BlockTooLong is the one variant that does not come straight from the shared grammar, so it says what actually happened rather than borrowing a codec error that would name the wrong thing.

Source§

fn from(error: FrameError) -> Self

Converts to this type from the input type.
Source§

impl From<HttpClientError> for ConnectError

Source§

fn from(source: HttpClientError) -> Self

Converts to this type from the input type.
Source§

impl From<HttpClientError> for ConnectError

Source§

fn from(source: HttpClientError) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more