agentguard_core/error.rs
1//! Error types for agentguard-core.
2//!
3//! Two parallel hierarchies are intentionally maintained: this crate
4//! (`agentguard_core::Error`) covers authorization-engine failures
5//! (Cedar parse, schema validation, decision evaluation, delegation token
6//! issues), while `agentguard_auth::AuthError` covers authentication
7//! failures (JWT, OIDC, API keys, DPoP, SPIFFE). Merging them would create
8//! a circular dependency between `agentguard-core` and `agentguard-auth`,
9//! since `agentguard-auth` depends on `agentguard-core` for the
10//! `Principal` and `AgentAction` types.
11//!
12//! Callers handling both (e.g. the AuthZEN server) match on each
13//! separately. `anyhow::Result` or `Box<dyn std::error::Error + Send +
14//! Sync>` is the right type for code paths that span both.
15//!
16//! When converting from one to the other, use
17//! `.map_err(|e| Error::Other(e.to_string()))`. A future v2.1 may add a
18//! unified error type behind a feature flag without breaking this design.
19
20use thiserror::Error;
21
22/// Result alias for agentguard-core fallible operations.
23pub type Result<T> = std::result::Result<T, Error>;
24
25/// Errors produced by agentguard-core.
26///
27/// `#[non_exhaustive]` lets the v2.x line add new variants without breaking
28/// downstream exhaustive matches.
29#[derive(Debug, Error, Clone, PartialEq, Eq)]
30#[non_exhaustive]
31pub enum Error {
32 #[error("io error: {0}")]
33 Io(String),
34
35 #[error("json error: {0}")]
36 Json(String),
37
38 #[error("policy parse error in {file}: {message}")]
39 PolicyParse {
40 /// Human-readable parse error.
41 message: String,
42 /// File or virtual file where the policy lives (e.g. `policy0.cedar`).
43 file: String,
44 },
45
46 #[error("schema error: {0}")]
47 Schema(String),
48
49 #[error("invalid principal: {0}")]
50 InvalidPrincipal(String),
51
52 #[error("invalid resource: {0}")]
53 InvalidResource(String),
54
55 #[error("invalid context: {0}")]
56 InvalidContext(String),
57
58 #[error("invalid delegation token: {0}")]
59 InvalidToken(String),
60
61 #[error("token expired at {0}")]
62 TokenExpired(String),
63
64 #[error("delegation token revoked: {0}")]
65 TokenRevoked(String),
66
67 #[error("token signature invalid: {reason}")]
68 TokenSignature {
69 /// Why the signature failed verification (bad signature, unknown kid, etc.).
70 reason: String,
71 },
72
73 #[error("token not yet valid (nbf={0})")]
74 TokenNotYetValid(String),
75
76 #[error("policy validation failed: {0}")]
77 Validation(String),
78
79 #[error("entities: {0}")]
80 Entities(String),
81
82 #[error("walk: {0}")]
83 Walk(String),
84
85 #[error("other: {0}")]
86 Other(String),
87}
88
89impl From<std::io::Error> for Error {
90 fn from(e: std::io::Error) -> Self {
91 Error::Io(e.to_string())
92 }
93}
94
95impl From<serde_json::Error> for Error {
96 fn from(e: serde_json::Error) -> Self {
97 Error::Json(e.to_string())
98 }
99}
100
101impl From<anyhow::Error> for Error {
102 fn from(e: anyhow::Error) -> Self {
103 Error::Other(e.to_string())
104 }
105}