1use std::fmt;
7use thiserror::Error;
8
9pub const JSON_PARSE_ERROR: i32 = -32700;
15
16pub const INVALID_REQUEST_ERROR: i32 = -32600;
18
19pub const METHOD_NOT_FOUND_ERROR: i32 = -32601;
21
22pub const INVALID_PARAMS_ERROR: i32 = -32602;
24
25pub const INTERNAL_ERROR: i32 = -32603;
27
28pub const TASK_NOT_FOUND_ERROR: i32 = -32001;
34
35pub const TASK_NOT_CANCELABLE_ERROR: i32 = -32002;
37
38pub const PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR: i32 = -32003;
40
41pub const UNSUPPORTED_OPERATION_ERROR: i32 = -32004;
43
44pub const CONTENT_TYPE_NOT_SUPPORTED_ERROR: i32 = -32005;
46
47pub const INVALID_AGENT_RESPONSE_ERROR: i32 = -32006;
49
50pub const AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED_ERROR: i32 = -32007;
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum A2aErrorCode {
60 JsonParseError,
62 InvalidRequest,
63 MethodNotFound,
64 InvalidParams,
65 InternalError,
66 TaskNotFound,
68 TaskNotCancelable,
69 PushNotificationNotSupported,
70 UnsupportedOperation,
71 ContentTypeNotSupported,
72 InvalidAgentResponse,
73 AuthenticatedExtendedCardNotConfigured,
74 Custom(i32),
76}
77
78impl From<A2aErrorCode> for i32 {
79 fn from(code: A2aErrorCode) -> Self {
80 match code {
81 A2aErrorCode::JsonParseError => JSON_PARSE_ERROR,
82 A2aErrorCode::InvalidRequest => INVALID_REQUEST_ERROR,
83 A2aErrorCode::MethodNotFound => METHOD_NOT_FOUND_ERROR,
84 A2aErrorCode::InvalidParams => INVALID_PARAMS_ERROR,
85 A2aErrorCode::InternalError => INTERNAL_ERROR,
86 A2aErrorCode::TaskNotFound => TASK_NOT_FOUND_ERROR,
87 A2aErrorCode::TaskNotCancelable => TASK_NOT_CANCELABLE_ERROR,
88 A2aErrorCode::PushNotificationNotSupported => PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR,
89 A2aErrorCode::UnsupportedOperation => UNSUPPORTED_OPERATION_ERROR,
90 A2aErrorCode::ContentTypeNotSupported => CONTENT_TYPE_NOT_SUPPORTED_ERROR,
91 A2aErrorCode::InvalidAgentResponse => INVALID_AGENT_RESPONSE_ERROR,
92 A2aErrorCode::AuthenticatedExtendedCardNotConfigured => AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED_ERROR,
93 A2aErrorCode::Custom(code) => code,
94 }
95 }
96}
97
98impl From<i32> for A2aErrorCode {
99 fn from(code: i32) -> Self {
100 match code {
101 JSON_PARSE_ERROR => A2aErrorCode::JsonParseError,
102 INVALID_REQUEST_ERROR => A2aErrorCode::InvalidRequest,
103 METHOD_NOT_FOUND_ERROR => A2aErrorCode::MethodNotFound,
104 INVALID_PARAMS_ERROR => A2aErrorCode::InvalidParams,
105 INTERNAL_ERROR => A2aErrorCode::InternalError,
106 TASK_NOT_FOUND_ERROR => A2aErrorCode::TaskNotFound,
107 TASK_NOT_CANCELABLE_ERROR => A2aErrorCode::TaskNotCancelable,
108 PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR => A2aErrorCode::PushNotificationNotSupported,
109 UNSUPPORTED_OPERATION_ERROR => A2aErrorCode::UnsupportedOperation,
110 CONTENT_TYPE_NOT_SUPPORTED_ERROR => A2aErrorCode::ContentTypeNotSupported,
111 INVALID_AGENT_RESPONSE_ERROR => A2aErrorCode::InvalidAgentResponse,
112 AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED_ERROR => A2aErrorCode::AuthenticatedExtendedCardNotConfigured,
113 other => A2aErrorCode::Custom(other),
114 }
115 }
116}
117
118impl fmt::Display for A2aErrorCode {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 match self {
121 A2aErrorCode::JsonParseError => write!(f, "JSON parse error"),
122 A2aErrorCode::InvalidRequest => write!(f, "Invalid request"),
123 A2aErrorCode::MethodNotFound => write!(f, "Method not found"),
124 A2aErrorCode::InvalidParams => write!(f, "Invalid params"),
125 A2aErrorCode::InternalError => write!(f, "Internal error"),
126 A2aErrorCode::TaskNotFound => write!(f, "Task not found"),
127 A2aErrorCode::TaskNotCancelable => write!(f, "Task not cancelable"),
128 A2aErrorCode::PushNotificationNotSupported => {
129 write!(f, "Push notifications not supported")
130 }
131 A2aErrorCode::UnsupportedOperation => write!(f, "Unsupported operation"),
132 A2aErrorCode::ContentTypeNotSupported => write!(f, "Content type not supported"),
133 A2aErrorCode::InvalidAgentResponse => write!(f, "Invalid agent response"),
134 A2aErrorCode::AuthenticatedExtendedCardNotConfigured => {
135 write!(f, "Authenticated extended card not configured")
136 }
137 A2aErrorCode::Custom(code) => write!(f, "Custom error ({code})"),
138 }
139 }
140}
141
142#[derive(Debug, Error)]
144pub enum A2aError {
145 #[error("JSON-RPC error ({code}): {message}")]
146 RpcError {
147 code: A2aErrorCode,
148 message: String,
149 #[source]
150 data: Option<Box<dyn std::error::Error + Send + Sync>>,
151 },
152
153 #[error("Task not found: {0}")]
154 TaskNotFound(String),
155
156 #[error("Task not cancelable: {0}")]
157 TaskNotCancelable(String),
158
159 #[error("Invalid task state transition: {from:?} -> {to:?}")]
160 InvalidStateTransition {
161 from: super::types::TaskState,
162 to: super::types::TaskState,
163 },
164
165 #[error("Unsupported operation: {0}")]
166 UnsupportedOperation(String),
167
168 #[error("Content type not supported: {0}")]
169 ContentTypeNotSupported(String),
170
171 #[error("Serialization error: {0}")]
172 Serialization(#[from] serde_json::Error),
173
174 #[error("Internal error: {0}")]
175 Internal(String),
176}
177
178impl A2aError {
179 pub fn code(&self) -> A2aErrorCode {
181 match self {
182 A2aError::RpcError { code, .. } => *code,
183 A2aError::TaskNotFound(_) => A2aErrorCode::TaskNotFound,
184 A2aError::TaskNotCancelable(_) => A2aErrorCode::TaskNotCancelable,
185 A2aError::InvalidStateTransition { .. } => A2aErrorCode::InvalidParams,
186 A2aError::UnsupportedOperation(_) => A2aErrorCode::UnsupportedOperation,
187 A2aError::ContentTypeNotSupported(_) => A2aErrorCode::ContentTypeNotSupported,
188 A2aError::Serialization(_) => A2aErrorCode::JsonParseError,
189 A2aError::Internal(_) => A2aErrorCode::InternalError,
190 }
191 }
192
193 pub fn rpc(code: A2aErrorCode, message: impl Into<String>) -> Self {
195 A2aError::RpcError { code, message: message.into(), data: None }
196 }
197
198 pub fn internal(message: impl Into<String>) -> Self {
200 A2aError::Internal(message.into())
201 }
202}
203
204pub type A2aResult<T> = Result<T, A2aError>;
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn test_error_code_conversion() {
213 assert_eq!(i32::from(A2aErrorCode::TaskNotFound), TASK_NOT_FOUND_ERROR);
214 assert_eq!(A2aErrorCode::from(TASK_NOT_FOUND_ERROR), A2aErrorCode::TaskNotFound);
215 }
216
217 #[test]
218 fn test_error_code_display() {
219 assert_eq!(A2aErrorCode::TaskNotFound.to_string(), "Task not found");
220 assert_eq!(A2aErrorCode::MethodNotFound.to_string(), "Method not found");
221 }
222
223 #[test]
224 fn test_a2a_error_code() {
225 let err = A2aError::TaskNotFound("task-123".to_string());
226 assert_eq!(err.code(), A2aErrorCode::TaskNotFound);
227 }
228
229 #[test]
230 fn test_custom_error_code() {
231 let custom = A2aErrorCode::Custom(-32099);
232 assert_eq!(i32::from(custom), -32099);
233 assert_eq!(custom.to_string(), "Custom error (-32099)");
234 }
235}