claude_code/errors.rs
1//! Error types for the Claude Code SDK.
2//!
3//! This module defines all error types that can occur during SDK operations,
4//! including connection failures, process errors, JSON parsing errors, and
5//! message parsing errors.
6
7use serde_json::Value;
8use thiserror::Error;
9
10/// General SDK error for validation and logic failures.
11///
12/// Used for errors that don't fit into more specific categories, such as
13/// invalid configuration or callback errors.
14#[derive(Debug, Error, Clone)]
15#[error("{message}")]
16pub struct ClaudeSDKError {
17 /// Human-readable error message.
18 pub message: String,
19}
20
21impl ClaudeSDKError {
22 /// Creates a new `ClaudeSDKError` with the given message.
23 ///
24 /// # Example
25 ///
26 /// ```rust
27 /// use claude_code::ClaudeSDKError;
28 ///
29 /// let err = ClaudeSDKError::new("invalid configuration");
30 /// assert_eq!(err.to_string(), "invalid configuration");
31 /// ```
32 pub fn new(message: impl Into<String>) -> Self {
33 Self {
34 message: message.into(),
35 }
36 }
37}
38
39/// Error establishing or maintaining a connection to the Claude Code CLI process.
40///
41/// Raised when the transport layer cannot connect, the process terminates
42/// unexpectedly, or stdin/stdout communication fails.
43#[derive(Debug, Error, Clone)]
44#[error("{message}")]
45pub struct CLIConnectionError {
46 /// Human-readable connection error message.
47 pub message: String,
48}
49
50impl CLIConnectionError {
51 /// Creates a new `CLIConnectionError` with the given message.
52 ///
53 /// # Example
54 ///
55 /// ```rust
56 /// use claude_code::CLIConnectionError;
57 ///
58 /// let err = CLIConnectionError::new("connection dropped");
59 /// assert!(err.to_string().contains("connection dropped"));
60 /// ```
61 pub fn new(message: impl Into<String>) -> Self {
62 Self {
63 message: message.into(),
64 }
65 }
66}
67
68/// Error when the Claude Code CLI executable cannot be found.
69///
70/// This error is raised if the CLI is not installed or not in the expected
71/// locations. Install Claude Code with `npm install -g @anthropic-ai/claude-code`,
72/// or provide a custom path via [`ClaudeAgentOptions::cli_path`](crate::ClaudeAgentOptions::cli_path).
73#[derive(Debug, Error, Clone)]
74#[error("{message}")]
75pub struct CLINotFoundError {
76 /// Human-readable not-found message.
77 pub message: String,
78 /// The path that was searched, if a specific path was configured.
79 pub cli_path: Option<String>,
80}
81
82impl CLINotFoundError {
83 /// Creates a new `CLINotFoundError` with the given message and optional path.
84 ///
85 /// # Example
86 ///
87 /// ```rust
88 /// use claude_code::CLINotFoundError;
89 ///
90 /// let err = CLINotFoundError::new("Claude Code not found", Some("/usr/local/bin/claude".to_string()));
91 /// assert!(err.to_string().contains("/usr/local/bin/claude"));
92 /// ```
93 pub fn new(message: impl Into<String>, cli_path: Option<String>) -> Self {
94 let base = message.into();
95 let message = match &cli_path {
96 Some(path) => format!("{base}: {path}"),
97 None => base,
98 };
99 Self { message, cli_path }
100 }
101}
102
103/// Error from the Claude Code CLI subprocess execution.
104///
105/// Contains the exit code and stderr output for debugging.
106#[derive(Debug, Error, Clone)]
107#[error("{message}")]
108pub struct ProcessError {
109 /// Human-readable process error message (may include code/stderr summary).
110 pub message: String,
111 /// The process exit code, if available.
112 pub exit_code: Option<i32>,
113 /// The stderr output from the process, if captured.
114 pub stderr: Option<String>,
115}
116
117impl ProcessError {
118 /// Creates a new `ProcessError` with the given message, exit code, and stderr.
119 ///
120 /// # Example
121 ///
122 /// ```rust
123 /// use claude_code::ProcessError;
124 ///
125 /// let err = ProcessError::new("Command failed", Some(1), Some("permission denied".to_string()));
126 /// assert!(err.to_string().contains("exit code: 1"));
127 /// ```
128 pub fn new(message: impl Into<String>, exit_code: Option<i32>, stderr: Option<String>) -> Self {
129 let base = message.into();
130 let mut message = base.clone();
131 if let Some(code) = exit_code {
132 message = format!("{message} (exit code: {code})");
133 }
134 if let Some(stderr_text) = &stderr {
135 message = format!("{message}\nError output: {stderr_text}");
136 }
137
138 Self {
139 message,
140 exit_code,
141 stderr,
142 }
143 }
144}
145
146/// Error decoding JSON from the CLI stdout stream.
147///
148/// Raised when the CLI outputs invalid JSON or when a JSON message exceeds
149/// the configured buffer size.
150#[derive(Debug, Error, Clone)]
151#[error("Failed to decode JSON: {preview}...")]
152pub struct CLIJSONDecodeError {
153 /// The raw line that failed to parse.
154 pub line: String,
155 /// The original parsing error description.
156 pub original_error: String,
157 /// A preview of the raw line (first 100 characters).
158 pub preview: String,
159}
160
161impl CLIJSONDecodeError {
162 /// Creates a new `CLIJSONDecodeError` with the raw line and error description.
163 ///
164 /// # Example
165 ///
166 /// ```rust
167 /// use claude_code::CLIJSONDecodeError;
168 ///
169 /// let err = CLIJSONDecodeError::new("{bad json}", "expected value");
170 /// assert!(err.preview.contains("{bad json}"));
171 /// ```
172 pub fn new(line: impl Into<String>, original_error: impl Into<String>) -> Self {
173 let line = line.into();
174 let preview: String = line.chars().take(100).collect();
175 Self {
176 line,
177 original_error: original_error.into(),
178 preview,
179 }
180 }
181}
182
183/// Error parsing a JSON message into a typed [`Message`](crate::Message).
184///
185/// Raised when a message from the CLI is missing required fields or
186/// has unexpected structure.
187#[derive(Debug, Error, Clone)]
188#[error("{message}")]
189pub struct MessageParseError {
190 /// Human-readable parse failure message.
191 pub message: String,
192 /// The raw JSON data that failed to parse, if available.
193 pub data: Option<Value>,
194}
195
196impl MessageParseError {
197 /// Creates a new `MessageParseError` with the given message and optional raw data.
198 ///
199 /// # Example
200 ///
201 /// ```rust
202 /// use claude_code::MessageParseError;
203 ///
204 /// let err = MessageParseError::new("missing field", None);
205 /// assert_eq!(err.to_string(), "missing field");
206 /// ```
207 pub fn new(message: impl Into<String>, data: Option<Value>) -> Self {
208 Self {
209 message: message.into(),
210 data,
211 }
212 }
213}
214
215/// Unified error type for all SDK operations.
216///
217/// This enum wraps all specific error types into a single type, enabling
218/// use of the `?` operator throughout the SDK.
219#[derive(Debug, Error)]
220pub enum Error {
221 /// General SDK error.
222 #[error(transparent)]
223 ClaudeSDK(#[from] ClaudeSDKError),
224 /// Connection error with the CLI process.
225 #[error(transparent)]
226 CLIConnection(#[from] CLIConnectionError),
227 /// CLI executable not found.
228 #[error(transparent)]
229 CLINotFound(#[from] CLINotFoundError),
230 /// CLI process execution error.
231 #[error(transparent)]
232 Process(#[from] ProcessError),
233 /// JSON decoding error from CLI output.
234 #[error(transparent)]
235 CLIJSONDecode(#[from] CLIJSONDecodeError),
236 /// Message parsing error.
237 #[error(transparent)]
238 MessageParse(#[from] MessageParseError),
239 /// Standard I/O error.
240 #[error(transparent)]
241 Io(#[from] std::io::Error),
242 /// JSON serialization/deserialization error.
243 #[error(transparent)]
244 Json(#[from] serde_json::Error),
245 /// Other errors not covered by specific variants.
246 #[error("{0}")]
247 Other(String),
248}
249
250/// A specialized `Result` type for SDK operations.
251pub type Result<T> = std::result::Result<T, Error>;