Skip to main content

khive_runtime/
error.rs

1//! Runtime error types.
2
3use std::fmt;
4
5use thiserror::Error;
6
7pub type RuntimeResult<T> = Result<T, RuntimeError>;
8
9/// A single missing pack dependency (ADR-037).
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct MissingPackDependency {
12    pub from: String,
13    pub requires: String,
14}
15
16impl fmt::Display for MissingPackDependency {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        write!(
19            f,
20            "pack '{}' requires '{}', but '{}' is not in the loaded pack set",
21            self.from, self.requires, self.requires
22        )
23    }
24}
25
26impl std::error::Error for MissingPackDependency {}
27
28/// Multiple missing pack dependencies collected into one error (ADR-037).
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct MissingPackDependencies {
31    pub missing: Vec<MissingPackDependency>,
32}
33
34impl fmt::Display for MissingPackDependencies {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        let parts: Vec<String> = self.missing.iter().map(ToString::to_string).collect();
37        write!(f, "{}", parts.join("; "))
38    }
39}
40
41impl std::error::Error for MissingPackDependencies {}
42
43/// Circular pack dependency detected during topological sort (ADR-037).
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct CircularPackDependency {
46    pub cycle: Vec<String>,
47}
48
49impl fmt::Display for CircularPackDependency {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        write!(
52            f,
53            "circular dependency detected among packs: {}",
54            self.cycle.join(" -> ")
55        )
56    }
57}
58
59impl std::error::Error for CircularPackDependency {}
60
61#[derive(Debug, Error)]
62pub enum RuntimeError {
63    #[error("storage: {0}")]
64    Storage(#[from] khive_storage::StorageError),
65
66    #[error("sqlite: {0}")]
67    Sqlite(#[from] khive_db::SqliteError),
68
69    #[error("query: {0}")]
70    Query(#[from] khive_query::QueryError),
71
72    #[error("not found: {0}")]
73    NotFound(String),
74
75    #[error("invalid input: {0}")]
76    InvalidInput(String),
77
78    #[error("unconfigured: {0} is not set")]
79    Unconfigured(String),
80
81    #[error("embedding: {0}")]
82    Embedding(#[from] lattice_embed::EmbedError),
83
84    #[error("ambiguous: {0}")]
85    Ambiguous(String),
86
87    #[error("internal: {0}")]
88    Internal(String),
89
90    #[error("missing pack dependency: {0}")]
91    MissingPackDependency(MissingPackDependency),
92
93    #[error("missing pack dependencies: {0}")]
94    MissingPackDependencies(MissingPackDependencies),
95
96    #[error("{0}")]
97    CircularPackDependency(CircularPackDependency),
98
99    #[error("pack '{name}' registered twice (indices {first_idx} and {second_idx})")]
100    PackRedeclared {
101        name: String,
102        first_idx: usize,
103        second_idx: usize,
104    },
105
106    /// Gate denied this verb invocation (ADR-035).
107    ///
108    /// Returned by `VerbRegistry::dispatch` when the configured `Gate` returns
109    /// `GateDecision::Deny`. The pack is never invoked. The `reason` field
110    /// carries the deny message produced by the gate implementation.
111    #[error("permission denied for verb {verb:?}: {reason}")]
112    PermissionDenied { verb: String, reason: String },
113
114    /// A structured [`khive_types::KhiveError`] converted into the runtime
115    /// layer. The full structured error is preserved so callers can inspect
116    /// `kind`, `code`, `details`, and `retry_hint` without information loss.
117    #[error("{0}")]
118    Khive(khive_types::KhiveError),
119}
120
121impl From<khive_types::KhiveError> for RuntimeError {
122    fn from(e: khive_types::KhiveError) -> Self {
123        Self::Khive(e)
124    }
125}