devboy_skills/error.rs
1//! Error types for the skills subsystem.
2
3use std::path::PathBuf;
4
5/// Result alias used throughout `devboy-skills`.
6pub type Result<T> = std::result::Result<T, SkillError>;
7
8/// Errors that can surface while loading, parsing, or installing a skill.
9#[derive(Debug, thiserror::Error)]
10pub enum SkillError {
11 /// The skill body did not open with a YAML frontmatter block
12 /// (`---` at line 1, closing `---` on a subsequent line).
13 #[error("skill `{skill}`: missing YAML frontmatter delimiter `---`")]
14 MissingFrontmatter {
15 /// Identifier of the offending skill (directory name).
16 skill: String,
17 },
18
19 /// The YAML frontmatter parsed but did not contain a required field.
20 #[error("skill `{skill}`: required frontmatter field `{field}` is missing")]
21 MissingRequiredField {
22 /// Identifier of the offending skill.
23 skill: String,
24 /// Name of the missing field.
25 field: &'static str,
26 },
27
28 /// The YAML frontmatter parsed but a field had the wrong type.
29 #[error("skill `{skill}`: frontmatter field `{field}` has invalid type ({reason})")]
30 InvalidFieldType {
31 /// Identifier of the offending skill.
32 skill: String,
33 /// Name of the offending field.
34 field: &'static str,
35 /// Why the value is rejected.
36 reason: String,
37 },
38
39 /// The `category` field references a category that is not in the
40 /// known enum. Provide one of the shipped categories (self-bootstrap,
41 /// issue-tracking, code-review, self-feedback, meeting-notes, messenger)
42 /// or extend [`crate::skill::Category`] before shipping a new one.
43 #[error("skill `{skill}`: unknown category `{category}`")]
44 UnknownCategory {
45 /// Identifier of the offending skill.
46 skill: String,
47 /// The string that failed to parse as a category.
48 category: String,
49 },
50
51 /// Underlying YAML parser failure (syntax error inside the frontmatter).
52 #[error("skill `{skill}`: invalid YAML in frontmatter: {source}")]
53 InvalidYaml {
54 /// Identifier of the offending skill.
55 skill: String,
56 /// Wrapped YAML error.
57 #[source]
58 source: serde_yaml::Error,
59 },
60
61 /// A skill lookup was asked for a name that the source does not know
62 /// about.
63 #[error("skill `{name}` not found in source `{source_name}`")]
64 NotFound {
65 /// Name that was requested.
66 name: String,
67 /// The source that was queried.
68 source_name: &'static str,
69 },
70
71 /// Filesystem operation failed while reading or writing a skill / manifest.
72 #[error("filesystem error at `{path}`: {source}")]
73 Io {
74 /// Path that was being accessed.
75 path: PathBuf,
76 /// Wrapped IO error.
77 #[source]
78 source: std::io::Error,
79 },
80
81 /// Manifest JSON parse failure.
82 #[error("manifest at `{path}`: invalid JSON ({source})")]
83 InvalidManifest {
84 /// Path to the offending manifest.
85 path: PathBuf,
86 /// Wrapped serde_json error.
87 #[source]
88 source: serde_json::Error,
89 },
90
91 /// Non-manifest JSON (de)serialisation failure — used by the trace
92 /// subsystem for `trace.jsonl` records and `meta.json` writes so
93 /// callers do not see a misleading "manifest … invalid JSON"
94 /// error for a record that is not a manifest.
95 #[error("failed to {operation} JSON at `{path}`: {source}")]
96 SerdeJson {
97 /// What was being done when the failure happened
98 /// (`serialise trace record`, `write session meta`, …).
99 operation: &'static str,
100 /// Path associated with the record.
101 path: PathBuf,
102 /// Wrapped serde_json error.
103 #[source]
104 source: serde_json::Error,
105 },
106}