claude_code_sdk_rust/
error.rs1use thiserror::Error;
2
3#[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#[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#[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#[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#[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#[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
161pub fn map_transport_error<E>(err: E) -> ClaudeSDKError
163where
164 E: std::error::Error + 'static,
165{
166 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
176pub type Result<T> = std::result::Result<T, ClaudeSDKError>;