1use thiserror::Error;
2
3use crate::domain::error_details::{ErrorDetail, FieldViolation};
4
5pub type Result<T, E = A2AError> = std::result::Result<T, E>;
10
11pub const PARSE_ERROR: i32 = -32700;
13pub const INVALID_REQUEST: i32 = -32600;
14pub const METHOD_NOT_FOUND: i32 = -32601;
15pub const INVALID_PARAMS: i32 = -32602;
16pub const INTERNAL_ERROR: i32 = -32603;
17
18pub const TASK_NOT_FOUND: i32 = -32001;
20pub const TASK_NOT_CANCELABLE: i32 = -32002;
21pub const PUSH_NOTIFICATION_NOT_SUPPORTED: i32 = -32003;
22pub const UNSUPPORTED_OPERATION: i32 = -32004;
23pub const CONTENT_TYPE_NOT_SUPPORTED: i32 = -32005;
24pub const INVALID_AGENT_RESPONSE: i32 = -32006;
25pub const AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED: i32 = -32007;
26
27pub const DATABASE_ERROR: i32 = -32100;
29pub const VERSION_CONFLICT: i32 = -32101;
31
32#[derive(Error, Debug)]
34pub enum A2AError {
35 #[error("JSON-RPC error: {code} - {message}")]
36 JsonRpc {
37 code: i32,
38 message: String,
39 data: Option<serde_json::Value>,
40 },
41
42 #[error("JSON parse error: {0}")]
43 JsonParse(#[from] serde_json::Error),
44
45 #[error("Invalid request: {0}")]
46 InvalidRequest(String),
47
48 #[error("Invalid parameters: {0}")]
49 InvalidParams(String),
50
51 #[error("Method not found: {0}")]
52 MethodNotFound(String),
53
54 #[error("Task not found: {0}")]
55 TaskNotFound(String),
56
57 #[error("Task not cancelable: {0}")]
58 TaskNotCancelable(String),
59
60 #[error("Push notification not supported")]
61 PushNotificationNotSupported,
62
63 #[error("Unsupported operation: {0}")]
64 UnsupportedOperation(String),
65
66 #[error("Content type not supported: {0}")]
67 ContentTypeNotSupported(String),
68
69 #[error("Invalid agent response: {0}")]
70 InvalidAgentResponse(String),
71
72 #[error("Authenticated extended card not configured")]
73 AuthenticatedExtendedCardNotConfigured,
74
75 #[error("Internal error: {0}")]
76 Internal(String),
77
78 #[error("Validation error in {field}: {message}")]
79 ValidationError { field: String, message: String },
80
81 #[error("Version conflict for task {id}: expected {expected}, found {actual}")]
82 VersionConflict {
83 id: String,
84 expected: u64,
85 actual: u64,
86 },
87
88 #[error("Database error: {0}")]
89 DatabaseError(String),
90
91 #[error("IO error: {0}")]
92 Io(#[from] std::io::Error),
93}
94
95impl A2AError {
96 pub fn to_jsonrpc_error(&self) -> serde_json::Value {
98 let (code, message) = match self {
99 A2AError::JsonParse(_) => (PARSE_ERROR, "Invalid JSON payload"),
100 A2AError::InvalidRequest(_) => (INVALID_REQUEST, "Request payload validation error"),
101 A2AError::MethodNotFound(_) => (METHOD_NOT_FOUND, "Method not found"),
102 A2AError::InvalidParams(_) => (INVALID_PARAMS, "Invalid parameters"),
103 A2AError::TaskNotFound(_) => (TASK_NOT_FOUND, "Task not found"),
104 A2AError::TaskNotCancelable(_) => (TASK_NOT_CANCELABLE, "Task cannot be canceled"),
105 A2AError::PushNotificationNotSupported => (
106 PUSH_NOTIFICATION_NOT_SUPPORTED,
107 "Push Notification is not supported",
108 ),
109 A2AError::UnsupportedOperation(_) => {
110 (UNSUPPORTED_OPERATION, "This operation is not supported")
111 }
112 A2AError::ContentTypeNotSupported(_) => {
113 (CONTENT_TYPE_NOT_SUPPORTED, "Incompatible content types")
114 }
115 A2AError::InvalidAgentResponse(_) => (INVALID_AGENT_RESPONSE, "Invalid agent response"),
116 A2AError::AuthenticatedExtendedCardNotConfigured => (
117 AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED,
118 "Authenticated Extended Card is not configured",
119 ),
120 A2AError::ValidationError { .. } => (INVALID_PARAMS, "Validation error"),
121 A2AError::VersionConflict { .. } => (VERSION_CONFLICT, "Task version conflict"),
122 A2AError::DatabaseError(_) => (DATABASE_ERROR, "Database error"),
123 A2AError::Internal(_) => (INTERNAL_ERROR, "Internal error"),
124 _ => (INTERNAL_ERROR, "Internal error"),
125 };
126
127 serde_json::json!({
128 "code": code,
129 "message": message,
130 "data": null,
131 })
132 }
133
134 pub fn reason_code(&self) -> &'static str {
137 match self {
138 A2AError::JsonRpc { .. } => "JSON_RPC_ERROR",
139 A2AError::JsonParse(_) => "PARSE_ERROR",
140 A2AError::InvalidRequest(_) => "INVALID_REQUEST",
141 A2AError::InvalidParams(_) => "INVALID_PARAMS",
142 A2AError::MethodNotFound(_) => "METHOD_NOT_FOUND",
143 A2AError::TaskNotFound(_) => "TASK_NOT_FOUND",
144 A2AError::TaskNotCancelable(_) => "TASK_NOT_CANCELABLE",
145 A2AError::PushNotificationNotSupported => "PUSH_NOTIFICATION_NOT_SUPPORTED",
146 A2AError::UnsupportedOperation(_) => "UNSUPPORTED_OPERATION",
147 A2AError::ContentTypeNotSupported(_) => "CONTENT_TYPE_NOT_SUPPORTED",
148 A2AError::InvalidAgentResponse(_) => "INVALID_AGENT_RESPONSE",
149 A2AError::AuthenticatedExtendedCardNotConfigured => {
150 "AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED"
151 }
152 A2AError::Internal(_) => "INTERNAL_ERROR",
153 A2AError::ValidationError { .. } => "VALIDATION_ERROR",
154 A2AError::VersionConflict { .. } => "VERSION_CONFLICT",
155 A2AError::DatabaseError(_) => "DATABASE_ERROR",
156 A2AError::Io(_) => "IO_ERROR",
157 }
158 }
159
160 pub fn error_details(&self) -> Vec<ErrorDetail> {
168 match self {
169 A2AError::ValidationError { field, message } => vec![
170 ErrorDetail::BadRequest {
171 field_violations: vec![FieldViolation::new(field, message)],
172 },
173 ErrorDetail::reason(self.reason_code()),
174 ],
175 A2AError::VersionConflict {
176 id,
177 expected,
178 actual,
179 } => {
180 let mut info = crate::domain::error_details::ErrorInfo::new(self.reason_code());
181 info = info
182 .with_metadata("task_id", id)
183 .with_metadata("expected", expected.to_string())
184 .with_metadata("actual", actual.to_string());
185 vec![ErrorDetail::ErrorInfo(info)]
186 }
187 _ => vec![ErrorDetail::reason(self.reason_code())],
188 }
189 }
190}