Skip to main content

stream_rs/
error.rs

1//! Error types for the streaming toolkit.
2
3use core::fmt;
4
5use alloc::string::String;
6
7/// Error produced when a JSON byte stream ends while a top-level value is still
8/// incomplete (see [`JsonSplitter::finish`](crate::incremental_json::JsonSplitter::finish)).
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[non_exhaustive]
11pub struct TruncatedJson {
12    /// The number of bytes that were buffered for the unfinished value.
13    pub buffered: usize,
14}
15
16impl fmt::Display for TruncatedJson {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        write!(
19            f,
20            "stream ended with {} buffered byte(s) of an incomplete top-level JSON value",
21            self.buffered
22        )
23    }
24}
25
26#[cfg(feature = "std")]
27impl std::error::Error for TruncatedJson {}
28
29/// Error produced by a [`JsonSplitter`](crate::incremental_json::JsonSplitter)
30/// running in **strict** mode when the byte stream violates top-level JSON
31/// framing — for example a stray closing bracket (`}` or `]`) at depth zero, as
32/// in `}{"a":1}`.
33///
34/// The default (non-strict) splitter never produces this error: it is a
35/// framing scanner, not a validator. Strict mode is opt-in via
36/// [`JsonSplitter::strict`](crate::incremental_json::JsonSplitter::strict) for
37/// callers that want adversarial framing rejected up front rather than passing
38/// a malformed value on to a real JSON parser.
39#[derive(Debug, Clone, PartialEq, Eq)]
40#[non_exhaustive]
41pub struct MalformedJson {
42    /// The offending byte (e.g. `b'}'` or `b']'`).
43    pub byte: u8,
44    /// How many top-level values had already been emitted before the violation,
45    /// which helps a caller locate the bad frame.
46    pub values_emitted: usize,
47}
48
49impl fmt::Display for MalformedJson {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        write!(
52            f,
53            "malformed top-level JSON framing: unexpected byte {:?} at depth 0 after {} value(s)",
54            self.byte as char, self.values_emitted
55        )
56    }
57}
58
59#[cfg(feature = "std")]
60impl std::error::Error for MalformedJson {}
61
62/// Errors produced while accumulating provider-specific streaming deltas.
63///
64/// `#[non_exhaustive]`: more variants may be added as further ordering or
65/// validation checks are introduced, without it being a breaking change.
66#[derive(Debug, Clone, PartialEq, Eq)]
67#[non_exhaustive]
68pub enum AccumulateError {
69    /// An event arrived in an order the state machine does not allow
70    /// (for example an Anthropic `content_block_delta` before `content_block_start`,
71    /// a block event before `message_start`, or any block event after `message_stop`).
72    UnexpectedEvent {
73        /// Human-readable description of what was received.
74        got: String,
75    },
76    /// A delta targeted a content block whose kind does not match the delta
77    /// (for example a `text_delta` applied to a `tool_use` block).
78    BlockKindMismatch {
79        /// Index of the content block.
80        index: usize,
81        /// The block kind that was expected by the delta.
82        expected: &'static str,
83        /// The kind the block actually has.
84        actual: &'static str,
85    },
86}
87
88impl fmt::Display for AccumulateError {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        match self {
91            Self::UnexpectedEvent { got } => write!(f, "unexpected event: {got}"),
92            Self::BlockKindMismatch {
93                index,
94                expected,
95                actual,
96            } => write!(
97                f,
98                "block kind mismatch at content block {index}: expected {expected}, got {actual}"
99            ),
100        }
101    }
102}
103
104#[cfg(feature = "std")]
105impl std::error::Error for AccumulateError {}