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}