Skip to main content

claude_code_sdk_rust/
error.rs

1use thiserror::Error;
2
3/// Base error type for SDK-specific failures
4#[derive(Debug, Error)]
5pub enum ClaudeSDKError {
6    #[error("CLI connection error: {0}")]
7    CLIConnection(#[from] CLIConnectionError),
8
9    #[error("CLI not found: {0}")]
10    CLINotFound(CLINotFoundError),
11
12    #[error("Process error: {0}")]
13    Process(#[from] ProcessError),
14
15    #[error("JSON decode error: {0}")]
16    CLIJSONDecode(#[from] CLIJSONDecodeError),
17
18    #[error("Message parse error: {0}")]
19    MessageParse(#[from] MessageParseError),
20
21    #[error("IO error: {0}")]
22    IO(#[from] std::io::Error),
23
24    #[error("Serialization error: {0}")]
25    Serialization(#[from] serde_json::Error),
26
27    #[error("Session error: {0}")]
28    Session(String),
29
30    #[error("MCP error: {0}")]
31    MCP(String),
32
33    #[error("Control request error: {subtype} - {message}")]
34    ControlRequest { subtype: String, message: String },
35
36    #[error("{0}")]
37    Other(String),
38}
39
40impl From<CLINotFoundError> for ClaudeSDKError {
41    fn from(err: CLINotFoundError) -> Self {
42        ClaudeSDKError::CLINotFound(err)
43    }
44}
45
46/// Reports failures when the SDK cannot connect to the CLI
47#[derive(Debug, Error)]
48#[error("CLI connection failed: {message}")]
49pub struct CLIConnectionError {
50    pub message: String,
51}
52
53impl CLIConnectionError {
54    pub fn new(message: impl Into<String>) -> Self {
55        Self {
56            message: message.into(),
57        }
58    }
59}
60
61/// Reports that the Claude CLI could not be located
62#[derive(Debug, Error)]
63#[error("CLI not found: {message} (path: {cli_path})")]
64pub struct CLINotFoundError {
65    pub message: String,
66    pub cli_path: String,
67}
68
69impl CLINotFoundError {
70    pub fn new(message: impl Into<String>, cli_path: impl Into<String>) -> Self {
71        Self {
72            message: message.into(),
73            cli_path: cli_path.into(),
74        }
75    }
76}
77
78/// Reports CLI process failures
79#[derive(Debug)]
80pub struct ProcessError {
81    pub message: String,
82    pub exit_code: Option<i32>,
83    pub stderr: String,
84}
85
86impl std::fmt::Display for ProcessError {
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        write!(f, "{}", self.message)?;
89        if let Some(code) = self.exit_code {
90            write!(f, " (exit code: {})", code)?;
91        }
92        if !self.stderr.is_empty() {
93            write!(f, "\nError output: {}", self.stderr)?;
94        }
95        Ok(())
96    }
97}
98
99impl std::error::Error for ProcessError {}
100
101impl ProcessError {
102    pub fn new(
103        message: impl Into<String>,
104        exit_code: Option<i32>,
105        stderr: impl Into<String>,
106    ) -> Self {
107        Self {
108            message: message.into(),
109            exit_code,
110            stderr: stderr.into(),
111        }
112    }
113}
114
115/// Reports malformed JSON from CLI stdout
116#[derive(Debug, Error)]
117#[error("Failed to decode JSON: {line}")]
118pub struct CLIJSONDecodeError {
119    pub line: String,
120    #[source]
121    pub original_error: serde_json::Error,
122}
123
124impl CLIJSONDecodeError {
125    pub fn new(line: impl Into<String>, original_error: serde_json::Error) -> Self {
126        let line = line.into();
127        let truncated = if line.len() > 100 {
128            format!("{}...", &line[..100])
129        } else {
130            line
131        };
132        Self {
133            line: truncated,
134            original_error,
135        }
136    }
137}
138
139/// Reports malformed, known CLI payloads
140#[derive(Debug, Error)]
141#[error("Message parse error: {message}")]
142pub struct MessageParseError {
143    pub message: String,
144    pub data: Option<serde_json::Map<String, serde_json::Value>>,
145}
146
147impl MessageParseError {
148    pub fn new(message: impl Into<String>) -> Self {
149        Self {
150            message: message.into(),
151            data: None,
152        }
153    }
154
155    pub fn with_data(mut self, data: serde_json::Map<String, serde_json::Value>) -> Self {
156        self.data = Some(data);
157        self
158    }
159}
160
161/// Map internal transport errors to public SDK errors
162pub fn map_transport_error<E>(err: E) -> ClaudeSDKError
163where
164    E: std::error::Error + 'static,
165{
166    // Try downcasting to specific error types
167    if let Some(e) =
168        (&err as &(dyn std::error::Error + 'static)).downcast_ref::<CLIConnectionError>()
169    {
170        return ClaudeSDKError::CLIConnection(CLIConnectionError::new(&e.message));
171    }
172
173    ClaudeSDKError::Other(err.to_string())
174}
175
176/// Result type alias for SDK operations
177pub type Result<T> = std::result::Result<T, ClaudeSDKError>;