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