1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! Error types for the Daimon agent framework.
use thiserror::Error;
/// The central error type for all Daimon operations.
///
/// Provider crates should map their transport-specific errors
/// (HTTP, gRPC, SDK) to [`DaimonError::Model`].
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum DaimonError {
/// An error originating from a model provider (API error, bad response, etc.).
#[error("model error: {0}")]
Model(String),
/// A tool failed during execution.
#[error("tool execution failed for '{tool}': {message}")]
ToolExecution {
/// Name of the tool that failed.
tool: String,
/// Description of the failure.
message: String,
},
/// The requested tool was not found in the registry.
#[error("tool '{0}' not found in registry")]
ToolNotFound(String),
/// Attempted to register a tool with a name that already exists.
#[error("duplicate tool '{0}' in registry")]
DuplicateTool(String),
/// The agent builder failed validation (e.g. missing required model).
#[error("agent builder validation failed: {0}")]
Builder(String),
/// The agent exceeded the configured maximum number of iterations.
#[error("max iterations ({0}) exceeded")]
MaxIterations(usize),
/// A serialization or deserialization error.
#[error("serialization error: {0}")]
Serialization(#[from] serde_json::Error),
/// Tool input failed JSON Schema validation.
#[error("schema validation failed for tool '{tool}': {errors}")]
SchemaValidation {
/// Name of the tool whose input failed validation.
tool: String,
/// Human-readable description of validation errors.
errors: String,
},
/// A stream was closed before completing.
#[error("stream closed unexpectedly")]
StreamClosed,
/// A request timed out.
#[error("request timed out after {0:?}")]
Timeout(std::time::Duration),
/// The operation was cancelled via a cancellation token.
#[error("operation cancelled")]
Cancelled,
/// An orchestration error (chain or graph execution failure).
#[error("orchestration error: {0}")]
Orchestration(String),
/// An MCP protocol error.
#[error("MCP error: {0}")]
Mcp(String),
/// The agent exceeded the configured spending budget.
#[error("budget exceeded: ${spent:.6} spent, limit was ${limit:.6}")]
BudgetExceeded {
/// How much has been spent so far (USD).
spent: f64,
/// The configured limit (USD).
limit: f64,
},
/// An input or output guardrail blocked the request.
#[error("guardrail blocked: {0}")]
GuardrailBlocked(String),
/// A storage backend operation failed (checkpoint store, memory backend,
/// broker state, or another persistence layer).
///
/// `transient` distinguishes failures that may succeed on retry
/// (connection refused, timeout, I/O contention) from permanent ones
/// (corrupt data, invalid keys, misconfiguration), so callers can decide
/// whether retrying is worthwhile without string-matching the message.
#[error("storage error: {message}")]
Storage {
/// Description of the storage failure.
message: String,
/// `true` when retrying the operation may succeed.
transient: bool,
},
/// A catch-all for other errors.
#[error("{0}")]
Other(String),
}
impl DaimonError {
/// Creates a permanent (non-retryable) [`DaimonError::Storage`] error,
/// e.g. corrupt persisted data or an invalid storage key.
pub fn storage(message: impl Into<String>) -> Self {
Self::Storage {
message: message.into(),
transient: false,
}
}
/// Creates a transient (retryable) [`DaimonError::Storage`] error,
/// e.g. a connection failure, timeout, or I/O error.
pub fn storage_transient(message: impl Into<String>) -> Self {
Self::Storage {
message: message.into(),
transient: true,
}
}
}
/// A type alias for `Result<T, DaimonError>`.
pub type Result<T> = std::result::Result<T, DaimonError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_storage_constructor_is_permanent() {
let err = DaimonError::storage("corrupt checkpoint");
assert!(matches!(
err,
DaimonError::Storage {
transient: false,
..
}
));
assert_eq!(err.to_string(), "storage error: corrupt checkpoint");
}
#[test]
fn test_storage_transient_constructor_is_retryable() {
let err = DaimonError::storage_transient("connection refused");
assert!(matches!(
err,
DaimonError::Storage {
transient: true,
..
}
));
assert_eq!(err.to_string(), "storage error: connection refused");
}
}