contextvm_sdk/transport/open_stream/errors.rs
1//! Error taxonomy for CEP-41 open streams.
2//!
3//! Mirrors the TypeScript SDK's error classes
4//! (`sdk/src/transport/open-stream/errors.ts`) so failure classification is
5//! identical across implementations. Surfaced through the crate-level
6//! [`crate::Error`] via a `#[from]` conversion.
7
8/// Errors raised while reading, writing, or sequencing an open stream.
9#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
10pub enum OpenStreamError {
11 /// The stream was aborted locally or by the remote peer (terminal).
12 #[error("Open stream aborted (token: {token}){}", .reason.as_deref().map(|r| format!(": {r}")).unwrap_or_default())]
13 Abort {
14 /// The `progressToken` of the aborted stream.
15 token: String,
16 /// Optional advisory reason supplied in the `abort` frame.
17 reason: Option<String>,
18 },
19
20 /// A stream violated local admission or buffering policy
21 /// (concurrency, buffered-chunk count, buffered+queued byte budget).
22 #[error("Open stream policy violation: {0}")]
23 Policy(String),
24
25 /// Frames violated CEP-41 lifecycle or ordering rules (non-increasing
26 /// progress, stale/duplicate `chunkIndex`, frame before `start`, frame
27 /// after a terminal close/abort, incomplete `close.lastChunkIndex`).
28 #[error("Open stream sequence error: {0}")]
29 Sequence(String),
30}
31
32impl OpenStreamError {
33 /// Construct an [`OpenStreamError::Abort`].
34 pub fn abort(token: impl Into<String>, reason: Option<String>) -> Self {
35 Self::Abort {
36 token: token.into(),
37 reason,
38 }
39 }
40
41 /// Returns `true` if this is a terminal [`abort`](OpenStreamError::Abort).
42 pub fn is_abort(&self) -> bool {
43 matches!(self, Self::Abort { .. })
44 }
45}
46
47// The crate-level `From<OpenStreamError> for crate::Error` conversion is
48// generated by the `#[from]` attribute on `Error::OpenStream` (see
49// `src/core/error.rs`), mirroring `OversizedTransferError`.