Skip to main content

Module error

Module error 

Source
Expand description

ClientError and the opaque wrapper types (HttpError, WebSocketError, InvalidHeaderValueError, ParseError, SerializeError) that hide reqwest, tokio_tungstenite, and the underlying JSON parser from this crate’s public API.

§SemVer policy

reqwest and tokio-tungstenite are private dependencies of this crate. Their types do not appear in any public function signature, variant payload, or From impl. The wrapper types (HttpError, WebSocketError, InvalidHeaderValueError) expose a curated set of diagnostic accessors that return primitive types only, so this crate can bump the underlying transport’s major version without breaking downstream callers.

Internal construction goes through pub(crate) helpers on ClientError (from_reqwest, from_ws, from_invalid_header) — downstream consumers cannot construct the transport-error variants and never need to.

§serde_json is a partially-wrapped dependency (bd:JMAP-6r7c.26)

ParseError and SerializeError wrap serde_json::Error for the Parse and Serialize variant payloads. The wrappers expose only primitive accessors (line, column, classify returning the workspace-local ParseCategory enum), so pattern-matching code on these variants is insulated from a future JSON-parser swap (e.g. simd-json, serde_json_lenient) — the variant payload type is stable across such a swap.

The asymmetry with HttpError / WebSocketError: extension client crates (jmap-mail-client, jmap-chat-client, etc.) need to surface their own JSON parse / serialize failures as ClientError::Parse and ClientError::Serialize, so this crate publishes ClientError::from_parse and ClientError::from_serialize as the construction path. Those constructors take a serde_json::Error in their signature, so serde_json remains a transitively-public dependency for crates that call those helpers directly. A future JSON-parser swap would deprecate from_parse(serde_json::Error) in favor of an analogous helper for the new parser; the variant payload type (ParseError) does not change.

§Do not simplify the wrappers (bd:JMAP-6r7c.16)

A future contributor reading this module may suggest “just put reqwest::Error in the variant — downstream users want the full reqwest API”. That is the wrong simplification. The wrapper-types pattern is load-bearing for five independent reasons; all five must be re-derived from first principles before the wrappers can be removed:

  1. SemVer-bump isolation. reqwest::Error and tungstenite::Error are #[non_exhaustive] from third-party crates that bump major versions independently of this crate. Exposing them in this crate’s public API turns every transitive reqwest major bump into a SemVer break for every downstream extension client (jmap-mail-client, jmap-chat-client, jmap-calendars-client, etc., all eight planned extensions).
  2. Transport replaceability. This crate may swap the HTTP / WebSocket transport entirely — ureq, hyper-util directly, a curl-backed transport for an unusual deployment — without breaking downstream. The only thing downstream binds to is the wrapper’s accessor signature; the wrapped type is private and can be replaced in-place.
  3. Curated accessor surface. Not every reqwest::Error method is mirrored. Adding new diagnostic surface (e.g. WebSocketError::close_code()) requires a deliberate pub fn decision in this file, which surfaces in code review. An unwrapped error would silently grow the surface every reqwest minor release.
  4. Opaque construction. The pub(crate) from_reqwest / from_ws / from_invalid_header helpers on ClientError mean downstream cannot construct the transport-error variants even if they wanted to. That keeps the variants genuinely opaque — no “well, the public field is a reqwest::Error, so downstream can match on it” loophole.
  5. Workspace policy alignment. This crate’s AGENTS.md “Design Constraints” table documents the wrappers as settled after bd:JMAP-6lsm.22. The workspace AGENTS.md “TLS stack” rule additionally forbids native-tls / openssl; allowing reqwest::Error to leak would re-couple downstream to the reqwest feature-set decisions this crate has already made.

The accessor set on each wrapper is deliberately minimal. Resist requests to “just expose reqwest::Error” without re-arguing all five reasons above.

Structs§

HttpError
HTTP transport error reported by the underlying HTTP client.
InvalidHeaderValueError
A header value (typically an authentication token) contained bytes that are not valid for an HTTP header.
ParseError
JSON deserialize / parse error reported by the underlying JSON parser.
SerializeError
JSON serialize error reported by the underlying JSON serializer.
WebSocketError
WebSocket transport error reported by the underlying WebSocket client.

Enums§

ClientError
Errors produced by the base JMAP client.
HttpErrorKind
Classification of an HttpError returned by HttpError::kind.
ParseCategory
Classification of a ParseError / SerializeError failure.
WebSocketErrorKind
Classification of a WebSocketError returned by WebSocketError::kind.