Skip to main content

chipzen_bot/
error.rs

1use thiserror::Error;
2
3/// Top-level error type for SDK operations. Variants describe the
4/// failure surface in terms users care about (connect failed, server
5/// dropped us mid-handshake, peer sent something unparseable) rather
6/// than transport internals.
7///
8/// Large variants (`Transport`, `Json`) are boxed so `Result<T, Error>`
9/// stays small on the success path. The boxing is invisible to callers
10/// thanks to the `From` impls below.
11#[derive(Debug, Error)]
12pub enum Error {
13    #[error("WebSocket transport error: {0}")]
14    Transport(Box<tokio_tungstenite::tungstenite::Error>),
15
16    #[error("connection closed before {context}")]
17    ConnectionClosed { context: &'static str },
18
19    #[error("invalid URL: {0}")]
20    InvalidUrl(#[from] url::ParseError),
21
22    #[error("malformed protocol message: {0}")]
23    Protocol(String),
24
25    #[error("JSON serialization failed: {0}")]
26    Json(Box<serde_json::Error>),
27
28    #[error("retry budget exhausted ({attempts} attempts) — last error: {last_error}")]
29    RetriesExhausted { attempts: u32, last_error: String },
30
31    /// Raised when `bot.decide()` panics and `safe_mode` is off.
32    ///
33    /// Distinguished from transport/connection errors so the caller treats it
34    /// as terminal (a deterministic bot bug, not a transient disconnect) and
35    /// does NOT reconnect-retry it. See chipzen-ai/chipzen-sdk#52.
36    #[error("bot decision error (safe_mode off): {0}")]
37    BotDecision(String),
38
39    /// Raised when a server-supplied gateway URL is cross-origin relative to
40    /// the lobby, or downgrades `wss` to `ws`. Refused so the bot token is
41    /// never sent to a different host or in cleartext.
42    #[error("untrusted gateway URL: {0}")]
43    UntrustedGateway(String),
44}
45
46impl From<tokio_tungstenite::tungstenite::Error> for Error {
47    fn from(err: tokio_tungstenite::tungstenite::Error) -> Self {
48        Error::Transport(Box::new(err))
49    }
50}
51
52impl From<serde_json::Error> for Error {
53    fn from(err: serde_json::Error) -> Self {
54        Error::Json(Box::new(err))
55    }
56}