Skip to main content

harness_core/
error.rs

1use thiserror::Error;
2
3#[derive(Debug, Error)]
4#[non_exhaustive]
5pub enum HarnessError {
6    #[error("model error: {0}")]
7    Model(#[from] ModelError),
8    #[error("tool error: {0}")]
9    Tool(#[from] ToolError),
10    #[error("guide error: {0}")]
11    Guide(#[from] GuideError),
12    #[error("sensor error: {0}")]
13    Sensor(#[from] SensorError),
14    #[error("compaction error: {0}")]
15    Compact(#[from] CompactError),
16    #[error("skill error: {0}")]
17    Skill(#[from] SkillError),
18    #[error("budget exhausted after {iters} iterations")]
19    BudgetExhausted { iters: u32 },
20    #[error("policy violation: {0}")]
21    Policy(String),
22    #[error("{0}")]
23    Other(String),
24}
25
26#[derive(Debug, Error)]
27pub enum ModelError {
28    #[error("transport: {0}")]
29    Transport(String),
30    #[error("invalid response: {0}")]
31    Invalid(String),
32    #[error("rate limited (retry after {retry_after_ms}ms)")]
33    RateLimited { retry_after_ms: u64 },
34    #[error("context overflow: needed {needed} tokens, window is {window}")]
35    ContextOverflow { needed: u32, window: u32 },
36}
37
38#[derive(Debug, Error)]
39pub enum ToolError {
40    /// `hint` names the nearest registered tool and lists the rest. It goes back
41    /// to the model verbatim, and it is the difference between a wasted round
42    /// trip and a single-turn correction — so it is part of the error, not a log
43    /// line beside it. Empty when there is nothing useful to say.
44    #[error("tool `{name}` not found{hint}")]
45    NotFound { name: String, hint: String },
46    #[error("invalid args for `{name}`: {reason}")]
47    InvalidArgs { name: String, reason: String },
48    #[error("execution failed: {0}")]
49    Exec(String),
50    #[error("permission denied: {0}")]
51    Permission(String),
52}
53
54#[derive(Debug, Error)]
55pub enum GuideError {
56    #[error("guide `{id}` failed: {reason}")]
57    Failed { id: String, reason: String },
58}
59
60#[derive(Debug, Error)]
61pub enum SensorError {
62    #[error("sensor `{id}` failed: {reason}")]
63    Failed { id: String, reason: String },
64}
65
66#[derive(Debug, Error)]
67pub enum CompactError {
68    #[error("compaction stage {stage:?} failed: {reason}")]
69    Failed { stage: String, reason: String },
70}
71
72#[derive(Debug, Error)]
73pub enum SkillError {
74    #[error("io error: {0}")]
75    Io(String),
76    #[error("invalid SKILL.md at {path}: {reason}")]
77    Invalid { path: String, reason: String },
78    #[error("name regex violation: `{name}` — {reason}")]
79    NameRegex { name: String, reason: String },
80    #[error("description too long: {len} > 1024")]
81    DescriptionTooLong { len: usize },
82    #[error("compatibility too long: {len} > 500")]
83    CompatibilityTooLong { len: usize },
84    #[error("name `{name}` does not match parent directory `{dir}`")]
85    NameDirMismatch { name: String, dir: String },
86    #[error("missing required field `{field}`")]
87    MissingField { field: String },
88    #[error("skill `{name}` already registered")]
89    Duplicate { name: String },
90}
91
92pub type Result<T, E = HarnessError> = std::result::Result<T, E>;