Skip to main content

brainwires_a2a/
error.rs

1//! A2A error types and JSON-RPC error codes.
2
3use serde::{Deserialize, Serialize};
4
5// ---------------------------------------------------------------------------
6// JSON-RPC error codes (spec-defined)
7// ---------------------------------------------------------------------------
8
9/// Invalid JSON payload.
10pub const JSON_PARSE_ERROR: i32 = -32700;
11/// Request payload validation error.
12pub const INVALID_REQUEST: i32 = -32600;
13/// Method not found.
14pub const METHOD_NOT_FOUND: i32 = -32601;
15/// Invalid parameters.
16pub const INVALID_PARAMS: i32 = -32602;
17/// Internal error.
18pub const INTERNAL_ERROR: i32 = -32603;
19/// Task not found.
20pub const TASK_NOT_FOUND: i32 = -32001;
21/// Task cannot be canceled.
22pub const TASK_NOT_CANCELABLE: i32 = -32002;
23/// Push notification is not supported.
24pub const PUSH_NOT_SUPPORTED: i32 = -32003;
25/// This operation is not supported.
26pub const UNSUPPORTED_OPERATION: i32 = -32004;
27/// Incompatible content types.
28pub const CONTENT_TYPE_NOT_SUPPORTED: i32 = -32005;
29/// Invalid agent response.
30pub const INVALID_AGENT_RESPONSE: i32 = -32006;
31/// Authenticated Extended Card is not configured.
32pub const EXTENDED_CARD_NOT_CONFIGURED: i32 = -32007;
33/// Extension support is required but not available.
34pub const EXTENSION_SUPPORT_REQUIRED: i32 = -32008;
35/// Protocol version is not supported.
36pub const VERSION_NOT_SUPPORTED: i32 = -32009;
37
38// ---------------------------------------------------------------------------
39// Error type
40// ---------------------------------------------------------------------------
41
42/// A2A protocol error.
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct A2aError {
45    /// Numeric error code.
46    pub code: i32,
47    /// Human-readable error message.
48    pub message: String,
49    /// Optional additional data.
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub data: Option<serde_json::Value>,
52}
53
54impl A2aError {
55    /// Create a new error from code and message.
56    pub fn new(code: i32, message: impl Into<String>) -> Self {
57        Self {
58            code,
59            message: message.into(),
60            data: None,
61        }
62    }
63
64    /// Attach extra data to the error.
65    pub fn with_data(mut self, data: serde_json::Value) -> Self {
66        self.data = Some(data);
67        self
68    }
69
70    /// Task not found error.
71    pub fn task_not_found(task_id: &str) -> Self {
72        Self::new(TASK_NOT_FOUND, format!("Task not found: {task_id}"))
73    }
74
75    /// Task not cancelable error.
76    pub fn task_not_cancelable(task_id: &str) -> Self {
77        Self::new(
78            TASK_NOT_CANCELABLE,
79            format!("Task cannot be canceled: {task_id}"),
80        )
81    }
82
83    /// Push notifications not supported error.
84    pub fn push_not_supported() -> Self {
85        Self::new(PUSH_NOT_SUPPORTED, "Push notifications are not supported")
86    }
87
88    /// Unsupported operation error.
89    pub fn unsupported_operation(detail: &str) -> Self {
90        Self::new(
91            UNSUPPORTED_OPERATION,
92            format!("Unsupported operation: {detail}"),
93        )
94    }
95
96    /// Content type not supported error.
97    pub fn content_type_not_supported(detail: &str) -> Self {
98        Self::new(
99            CONTENT_TYPE_NOT_SUPPORTED,
100            format!("Content type not supported: {detail}"),
101        )
102    }
103
104    /// Invalid request error.
105    pub fn invalid_request(detail: impl Into<String>) -> Self {
106        Self::new(INVALID_REQUEST, detail)
107    }
108
109    /// Internal error.
110    pub fn internal(message: impl Into<String>) -> Self {
111        Self::new(INTERNAL_ERROR, message)
112    }
113
114    /// Method not found error.
115    pub fn method_not_found(method: &str) -> Self {
116        Self::new(METHOD_NOT_FOUND, format!("Method not found: {method}"))
117    }
118
119    /// Invalid params error.
120    pub fn invalid_params(detail: impl Into<String>) -> Self {
121        Self::new(INVALID_PARAMS, detail)
122    }
123
124    /// Parse error.
125    pub fn parse_error(detail: impl Into<String>) -> Self {
126        Self::new(JSON_PARSE_ERROR, detail)
127    }
128
129    /// Extended card not configured.
130    pub fn extended_card_not_configured() -> Self {
131        Self::new(
132            EXTENDED_CARD_NOT_CONFIGURED,
133            "Authenticated Extended Card is not configured",
134        )
135    }
136
137    /// Extension support is required but not available.
138    pub fn extension_support_required() -> Self {
139        Self::new(
140            EXTENSION_SUPPORT_REQUIRED,
141            "Extension support is required but not available",
142        )
143    }
144
145    /// Protocol version is not supported.
146    pub fn version_not_supported() -> Self {
147        Self::new(
148            VERSION_NOT_SUPPORTED,
149            "Protocol version is not supported",
150        )
151    }
152}
153
154impl std::fmt::Display for A2aError {
155    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156        write!(f, "A2A error {}: {}", self.code, self.message)
157    }
158}
159
160impl std::error::Error for A2aError {}
161
162impl From<serde_json::Error> for A2aError {
163    fn from(err: serde_json::Error) -> Self {
164        Self::parse_error(err.to_string())
165    }
166}
167
168impl From<anyhow::Error> for A2aError {
169    fn from(err: anyhow::Error) -> Self {
170        Self::internal(err.to_string())
171    }
172}