Skip to main content

zeph_a2a/
error.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use crate::jsonrpc::JsonRpcError;
5
6#[derive(Debug, thiserror::Error)]
7pub enum A2aError {
8    #[error("HTTP request failed: {0}")]
9    Http(#[from] reqwest::Error),
10
11    #[error("JSON serialization/deserialization failed: {0}")]
12    Json(#[from] serde_json::Error),
13
14    #[error("JSON-RPC error {code}: {message}")]
15    JsonRpc { code: i32, message: String },
16
17    #[error("agent discovery failed for {url}: {reason}")]
18    Discovery { url: String, reason: String },
19
20    #[error("SSE stream error: {0}")]
21    Stream(String),
22
23    #[error("server error: {0}")]
24    Server(String),
25
26    #[error("security policy violation: {0}")]
27    Security(String),
28}
29
30impl From<JsonRpcError> for A2aError {
31    fn from(e: JsonRpcError) -> Self {
32        Self::JsonRpc {
33            code: e.code,
34            message: e.message,
35        }
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    #[test]
44    fn from_jsonrpc_error() {
45        let rpc_err = JsonRpcError {
46            code: -32001,
47            message: "task not found".into(),
48            data: None,
49        };
50        let err: A2aError = rpc_err.into();
51        match err {
52            A2aError::JsonRpc { code, message } => {
53                assert_eq!(code, -32001);
54                assert_eq!(message, "task not found");
55            }
56            _ => panic!("expected JsonRpc variant"),
57        }
58    }
59
60    #[test]
61    fn error_display() {
62        let err = A2aError::Discovery {
63            url: "http://example.com".into(),
64            reason: "connection refused".into(),
65        };
66        assert_eq!(
67            err.to_string(),
68            "agent discovery failed for http://example.com: connection refused"
69        );
70
71        let err = A2aError::Stream("unexpected EOF".into());
72        assert_eq!(err.to_string(), "SSE stream error: unexpected EOF");
73    }
74
75    #[test]
76    fn security_error_display() {
77        let err = A2aError::Security("TLS required but endpoint uses HTTP".into());
78        assert_eq!(
79            err.to_string(),
80            "security policy violation: TLS required but endpoint uses HTTP"
81        );
82    }
83
84    #[test]
85    fn from_serde_json_error() {
86        let json_err = serde_json::from_str::<serde_json::Value>("invalid").unwrap_err();
87        let err: A2aError = json_err.into();
88        assert!(matches!(err, A2aError::Json(_)));
89    }
90}