agent_client_protocol/
error.rs

1//! Error handling for the Agent Client Protocol.
2//!
3//! This module provides error types and codes following the JSON-RPC 2.0 specification,
4//! with additional protocol-specific error codes for authentication and other ACP-specific scenarios.
5//!
6//! All methods in the protocol follow standard JSON-RPC 2.0 error handling:
7//! - Successful responses include a `result` field
8//! - Errors include an `error` object with `code` and `message`
9//! - Notifications never receive responses (success or error)
10//!
11//! See: [Error Handling](https://agentclientprotocol.com/protocol/overview#error-handling)
12
13use std::fmt::Display;
14
15use schemars::JsonSchema;
16use serde::{Deserialize, Serialize};
17
18/// JSON-RPC error object.
19///
20/// Represents an error that occurred during method execution, following the
21/// JSON-RPC 2.0 error object specification with optional additional data.
22///
23/// See protocol docs: [JSON-RPC Error Object](https://www.jsonrpc.org/specification#error_object)
24#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
25pub struct Error {
26    /// A number indicating the error type that occurred.
27    /// This must be an integer as defined in the JSON-RPC specification.
28    pub code: i32,
29    /// A string providing a short description of the error.
30    /// The message should be limited to a concise single sentence.
31    pub message: String,
32    /// Optional primitive or structured value that contains additional information about the error.
33    /// This may include debugging information or context-specific details.
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub data: Option<serde_json::Value>,
36}
37
38impl Error {
39    /// Creates a new error with the given code and message.
40    ///
41    /// The code parameter can be an `ErrorCode` constant or a tuple of (code, message).
42    pub fn new(code: impl Into<(i32, String)>) -> Self {
43        let (code, message) = code.into();
44        Error {
45            code,
46            message,
47            data: None,
48        }
49    }
50
51    /// Adds additional data to the error.
52    ///
53    /// This method is chainable and allows attaching context-specific information
54    /// to help with debugging or provide more details about the error.
55    #[must_use]
56    pub fn with_data(mut self, data: impl Into<serde_json::Value>) -> Self {
57        self.data = Some(data.into());
58        self
59    }
60
61    /// Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.
62    #[must_use]
63    pub fn parse_error() -> Self {
64        Error::new(ErrorCode::PARSE_ERROR)
65    }
66
67    /// The JSON sent is not a valid Request object.
68    #[must_use]
69    pub fn invalid_request() -> Self {
70        Error::new(ErrorCode::INVALID_REQUEST)
71    }
72
73    /// The method does not exist / is not available.
74    #[must_use]
75    pub fn method_not_found() -> Self {
76        Error::new(ErrorCode::METHOD_NOT_FOUND)
77    }
78
79    /// Invalid method parameter(s).
80    #[must_use]
81    pub fn invalid_params() -> Self {
82        Error::new(ErrorCode::INVALID_PARAMS)
83    }
84
85    /// Internal JSON-RPC error.
86    #[must_use]
87    pub fn internal_error() -> Self {
88        Error::new(ErrorCode::INTERNAL_ERROR)
89    }
90
91    /// Authentication required.
92    #[must_use]
93    pub fn auth_required() -> Self {
94        Error::new(ErrorCode::AUTH_REQUIRED)
95    }
96
97    /// Converts a standard error into an internal JSON-RPC error.
98    ///
99    /// The error's string representation is included as additional data.
100    pub fn into_internal_error(err: impl std::error::Error) -> Self {
101        Error::internal_error().with_data(err.to_string())
102    }
103}
104
105/// Predefined error codes for common JSON-RPC and ACP-specific errors.
106///
107/// These codes follow the JSON-RPC 2.0 specification for standard errors
108/// and use the reserved range (-32000 to -32099) for protocol-specific errors.
109#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
110pub struct ErrorCode {
111    /// The numeric error code.
112    pub code: i32,
113    /// The standard error message for this code.
114    pub message: &'static str,
115}
116
117impl ErrorCode {
118    /// Invalid JSON was received by the server.
119    /// An error occurred on the server while parsing the JSON text.
120    pub const PARSE_ERROR: ErrorCode = ErrorCode {
121        code: -32700,
122        message: "Parse error",
123    };
124
125    /// The JSON sent is not a valid Request object.
126    pub const INVALID_REQUEST: ErrorCode = ErrorCode {
127        code: -32600,
128        message: "Invalid Request",
129    };
130
131    /// The method does not exist or is not available.
132    pub const METHOD_NOT_FOUND: ErrorCode = ErrorCode {
133        code: -32601,
134        message: "Method not found",
135    };
136
137    /// Invalid method parameter(s).
138    pub const INVALID_PARAMS: ErrorCode = ErrorCode {
139        code: -32602,
140        message: "Invalid params",
141    };
142
143    /// Internal JSON-RPC error.
144    /// Reserved for implementation-defined server errors.
145    pub const INTERNAL_ERROR: ErrorCode = ErrorCode {
146        code: -32603,
147        message: "Internal error",
148    };
149
150    /// Authentication is required before this operation can be performed.
151    /// This is an ACP-specific error code in the reserved range.
152    pub const AUTH_REQUIRED: ErrorCode = ErrorCode {
153        code: -32000,
154        message: "Authentication required",
155    };
156}
157
158impl From<ErrorCode> for (i32, String) {
159    fn from(error_code: ErrorCode) -> Self {
160        (error_code.code, error_code.message.to_string())
161    }
162}
163
164impl From<ErrorCode> for Error {
165    fn from(error_code: ErrorCode) -> Self {
166        Error::new(error_code)
167    }
168}
169
170impl std::error::Error for Error {}
171
172impl Display for Error {
173    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174        if self.message.is_empty() {
175            write!(f, "{}", self.code)?;
176        } else {
177            write!(f, "{}", self.message)?;
178        }
179
180        if let Some(data) = &self.data {
181            let pretty = serde_json::to_string_pretty(data).unwrap_or_else(|_| data.to_string());
182            write!(f, ": {pretty}")?;
183        }
184
185        Ok(())
186    }
187}
188
189impl From<anyhow::Error> for Error {
190    fn from(error: anyhow::Error) -> Self {
191        Error::into_internal_error(&*error)
192    }
193}
194
195impl From<serde_json::Error> for Error {
196    fn from(error: serde_json::Error) -> Self {
197        Error::invalid_params().with_data(error.to_string())
198    }
199}