1use std::fmt;
11
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
21#[serde(into = "i32", try_from = "i32")]
22#[non_exhaustive]
23pub enum ErrorCode {
24 ParseError = -32700,
27 InvalidRequest = -32600,
29 MethodNotFound = -32601,
31 InvalidParams = -32602,
33 InternalError = -32603,
35
36 TaskNotFound = -32001,
39 TaskNotCancelable = -32002,
41 PushNotificationNotSupported = -32003,
43 UnsupportedOperation = -32004,
45 ContentTypeNotSupported = -32005,
47 InvalidAgentResponse = -32006,
49 ExtendedAgentCardNotConfigured = -32007,
51 ExtensionSupportRequired = -32008,
53 VersionNotSupported = -32009,
55}
56
57impl ErrorCode {
58 #[must_use]
60 pub const fn as_i32(self) -> i32 {
61 self as i32
62 }
63
64 #[must_use]
66 pub const fn default_message(self) -> &'static str {
67 match self {
68 Self::ParseError => "Parse error",
69 Self::InvalidRequest => "Invalid request",
70 Self::MethodNotFound => "Method not found",
71 Self::InvalidParams => "Invalid params",
72 Self::InternalError => "Internal error",
73 Self::TaskNotFound => "Task not found",
74 Self::TaskNotCancelable => "Task not cancelable",
75 Self::PushNotificationNotSupported => "Push notification not supported",
76 Self::UnsupportedOperation => "Unsupported operation",
77 Self::ContentTypeNotSupported => "Content type not supported",
78 Self::InvalidAgentResponse => "Invalid agent response",
79 Self::ExtendedAgentCardNotConfigured => "Extended agent card not configured",
80 Self::ExtensionSupportRequired => "Extension support required",
81 Self::VersionNotSupported => "Version not supported",
82 }
83 }
84}
85
86impl From<ErrorCode> for i32 {
87 fn from(code: ErrorCode) -> Self {
88 code as Self
89 }
90}
91
92impl TryFrom<i32> for ErrorCode {
93 type Error = i32;
94
95 fn try_from(v: i32) -> Result<Self, Self::Error> {
96 match v {
97 -32700 => Ok(Self::ParseError),
98 -32600 => Ok(Self::InvalidRequest),
99 -32601 => Ok(Self::MethodNotFound),
100 -32602 => Ok(Self::InvalidParams),
101 -32603 => Ok(Self::InternalError),
102 -32001 => Ok(Self::TaskNotFound),
103 -32002 => Ok(Self::TaskNotCancelable),
104 -32003 => Ok(Self::PushNotificationNotSupported),
105 -32004 => Ok(Self::UnsupportedOperation),
106 -32005 => Ok(Self::ContentTypeNotSupported),
107 -32006 => Ok(Self::InvalidAgentResponse),
108 -32007 => Ok(Self::ExtendedAgentCardNotConfigured),
109 -32008 => Ok(Self::ExtensionSupportRequired),
110 -32009 => Ok(Self::VersionNotSupported),
111 other => Err(other),
112 }
113 }
114}
115
116impl fmt::Display for ErrorCode {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 write!(f, "{} ({})", self.default_message(), self.as_i32())
119 }
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
129#[non_exhaustive]
130pub struct A2aError {
131 pub code: ErrorCode,
133 pub message: String,
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub data: Option<serde_json::Value>,
138}
139
140impl A2aError {
141 #[must_use]
143 pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
144 Self {
145 code,
146 message: message.into(),
147 data: None,
148 }
149 }
150
151 #[must_use]
153 pub fn with_data(code: ErrorCode, message: impl Into<String>, data: serde_json::Value) -> Self {
154 Self {
155 code,
156 message: message.into(),
157 data: Some(data),
158 }
159 }
160
161 #[must_use]
165 pub fn task_not_found(task_id: impl fmt::Display) -> Self {
166 Self::new(
167 ErrorCode::TaskNotFound,
168 format!("Task not found: {task_id}"),
169 )
170 }
171
172 #[must_use]
174 pub fn task_not_cancelable(task_id: impl fmt::Display) -> Self {
175 Self::new(
176 ErrorCode::TaskNotCancelable,
177 format!("Task cannot be canceled: {task_id}"),
178 )
179 }
180
181 #[must_use]
183 pub fn internal(msg: impl Into<String>) -> Self {
184 Self::new(ErrorCode::InternalError, msg)
185 }
186
187 #[must_use]
189 pub fn invalid_params(msg: impl Into<String>) -> Self {
190 Self::new(ErrorCode::InvalidParams, msg)
191 }
192
193 #[must_use]
195 pub fn unsupported_operation(msg: impl Into<String>) -> Self {
196 Self::new(ErrorCode::UnsupportedOperation, msg)
197 }
198
199 #[must_use]
201 pub fn parse_error(msg: impl Into<String>) -> Self {
202 Self::new(ErrorCode::ParseError, msg)
203 }
204
205 #[must_use]
207 pub fn invalid_agent_response(msg: impl Into<String>) -> Self {
208 Self::new(ErrorCode::InvalidAgentResponse, msg)
209 }
210
211 #[must_use]
213 pub fn extended_card_not_configured(msg: impl Into<String>) -> Self {
214 Self::new(ErrorCode::ExtendedAgentCardNotConfigured, msg)
215 }
216}
217
218impl fmt::Display for A2aError {
219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220 write!(f, "[{}] {}", self.code.as_i32(), self.message)
221 }
222}
223
224impl std::error::Error for A2aError {}
225
226pub type A2aResult<T> = Result<T, A2aError>;
230
231#[cfg(test)]
234mod tests {
235 use super::*;
236
237 #[test]
238 fn error_code_roundtrip() {
239 let code = ErrorCode::TaskNotFound;
240 let n: i32 = code.into();
241 assert_eq!(n, -32001);
242 assert_eq!(ErrorCode::try_from(n), Ok(ErrorCode::TaskNotFound));
243 }
244
245 #[test]
246 fn error_code_unknown_value() {
247 assert!(ErrorCode::try_from(-99999).is_err());
248 }
249
250 #[test]
251 fn a2a_error_display() {
252 let err = A2aError::task_not_found("abc123");
253 let s = err.to_string();
254 assert!(s.contains("-32001"), "expected code in display: {s}");
255 assert!(s.contains("abc123"), "expected task id in display: {s}");
256 }
257
258 #[test]
259 fn a2a_error_serialization() {
260 let err = A2aError::internal("something went wrong");
261 let json = serde_json::to_string(&err).expect("serialize");
262 let back: A2aError = serde_json::from_str(&json).expect("deserialize");
263 assert_eq!(back.code, ErrorCode::InternalError);
264 assert_eq!(back.message, "something went wrong");
265 assert!(back.data.is_none());
266 }
267
268 #[test]
269 fn a2a_error_with_data() {
270 let data = serde_json::json!({"detail": "extra info"});
271 let err = A2aError::with_data(ErrorCode::InvalidParams, "bad input", data.clone());
272 let json = serde_json::to_string(&err).expect("serialize");
273 assert!(json.contains("\"data\""), "data field should be present");
274 let back: A2aError = serde_json::from_str(&json).expect("deserialize");
275 assert_eq!(back.data, Some(data));
276 }
277}