Skip to main content

chronicle/
error.rs

1use snafu::Snafu;
2use std::path::PathBuf;
3
4#[derive(Debug, Snafu)]
5#[snafu(visibility(pub), module(chronicle_error))]
6pub enum ChronicleError {
7    #[snafu(display("not a git repository: {}", path.display()))]
8    NotARepository {
9        path: PathBuf,
10        #[snafu(implicit)]
11        location: snafu::Location,
12    },
13
14    #[snafu(display("chronicle not initialized (run `git chronicle init` first)"))]
15    NotInitialized {
16        #[snafu(implicit)]
17        location: snafu::Location,
18    },
19
20    #[snafu(display("git error: {source}"))]
21    Git {
22        source: GitError,
23        #[snafu(implicit)]
24        location: snafu::Location,
25    },
26
27    #[snafu(display("provider error: {source}"))]
28    Provider {
29        source: ProviderError,
30        #[snafu(implicit)]
31        location: snafu::Location,
32    },
33
34    #[snafu(display("agent error: {source}"))]
35    Agent {
36        source: AgentError,
37        #[snafu(implicit)]
38        location: snafu::Location,
39    },
40
41    #[snafu(display("config error: {message}"))]
42    Config {
43        message: String,
44        #[snafu(implicit)]
45        location: snafu::Location,
46    },
47
48    #[snafu(display("IO error: {source}"))]
49    Io {
50        source: std::io::Error,
51        #[snafu(implicit)]
52        location: snafu::Location,
53    },
54
55    #[snafu(display("JSON error: {source}"))]
56    Json {
57        source: serde_json::Error,
58        #[snafu(implicit)]
59        location: snafu::Location,
60    },
61
62    #[snafu(display("annotation validation error: {message}"))]
63    Validation {
64        message: String,
65        #[snafu(implicit)]
66        location: snafu::Location,
67    },
68
69    #[snafu(display("setup error: {source}"))]
70    Setup {
71        source: SetupError,
72        #[snafu(implicit)]
73        location: snafu::Location,
74    },
75}
76
77#[derive(Debug, Snafu)]
78#[snafu(visibility(pub), module(git_error))]
79pub enum GitError {
80    #[snafu(display("git command failed: {message}"))]
81    CommandFailed {
82        message: String,
83        #[snafu(implicit)]
84        location: snafu::Location,
85    },
86
87    #[snafu(display("commit not found: {sha}"))]
88    CommitNotFound {
89        sha: String,
90        #[snafu(implicit)]
91        location: snafu::Location,
92    },
93
94    #[snafu(display("file not found: {path} at {commit}"))]
95    FileNotFound {
96        path: String,
97        commit: String,
98        #[snafu(implicit)]
99        location: snafu::Location,
100    },
101
102    #[snafu(display("notes ref missing: {refname}"))]
103    NotesRefMissing {
104        refname: String,
105        #[snafu(implicit)]
106        location: snafu::Location,
107    },
108
109    #[snafu(display("diff parse error: {message}"))]
110    DiffParse {
111        message: String,
112        #[snafu(implicit)]
113        location: snafu::Location,
114    },
115
116    #[snafu(display("IO error: {source}"))]
117    Io {
118        source: std::io::Error,
119        #[snafu(implicit)]
120        location: snafu::Location,
121    },
122}
123
124#[derive(Debug, Snafu)]
125#[snafu(visibility(pub), module(provider_error))]
126pub enum ProviderError {
127    #[snafu(display("no credentials found for any provider"))]
128    NoCredentials {
129        #[snafu(implicit)]
130        location: snafu::Location,
131    },
132
133    #[snafu(display("authentication failed: {message}"))]
134    AuthFailed {
135        message: String,
136        #[snafu(implicit)]
137        location: snafu::Location,
138    },
139
140    #[snafu(display("rate limited, retry after {retry_after_secs}s"))]
141    RateLimited {
142        retry_after_secs: u64,
143        #[snafu(implicit)]
144        location: snafu::Location,
145    },
146
147    #[snafu(display("request timeout"))]
148    Timeout {
149        #[snafu(implicit)]
150        location: snafu::Location,
151    },
152
153    #[snafu(display("API error: {message}"))]
154    Api {
155        message: String,
156        #[snafu(implicit)]
157        location: snafu::Location,
158    },
159
160    #[snafu(display("failed to parse response: {message}"))]
161    ParseResponse {
162        message: String,
163        #[snafu(implicit)]
164        location: snafu::Location,
165    },
166
167    #[snafu(display("HTTP error: {source}, at {location}"))]
168    Http {
169        source: Box<ureq::Error>,
170        #[snafu(implicit)]
171        location: snafu::Location,
172    },
173
174    #[snafu(display("retries exhausted after {attempts} attempts"))]
175    RetriesExhausted {
176        attempts: u32,
177        #[snafu(implicit)]
178        location: snafu::Location,
179    },
180}
181
182#[derive(Debug, Snafu)]
183#[snafu(visibility(pub), module(agent_error))]
184pub enum AgentError {
185    #[snafu(display("provider error: {source}"))]
186    Provider {
187        source: ProviderError,
188        #[snafu(implicit)]
189        location: snafu::Location,
190    },
191
192    #[snafu(display("no annotations emitted by agent"))]
193    NoAnnotations {
194        #[snafu(implicit)]
195        location: snafu::Location,
196    },
197
198    #[snafu(display("max turns exceeded ({turns})"))]
199    MaxTurnsExceeded {
200        turns: u32,
201        #[snafu(implicit)]
202        location: snafu::Location,
203    },
204
205    #[snafu(display("invalid annotation: {message}"))]
206    InvalidAnnotation {
207        message: String,
208        #[snafu(implicit)]
209        location: snafu::Location,
210    },
211
212    #[snafu(display("git error: {source}"))]
213    Git {
214        source: GitError,
215        #[snafu(implicit)]
216        location: snafu::Location,
217    },
218
219    #[snafu(display("JSON error: {source}"))]
220    Json {
221        source: serde_json::Error,
222        #[snafu(implicit)]
223        location: snafu::Location,
224    },
225}
226
227#[derive(Debug, Snafu)]
228#[snafu(visibility(pub), module(setup_error))]
229pub enum SetupError {
230    #[snafu(display("home directory not found, at {location}"))]
231    NoHomeDirectory {
232        #[snafu(implicit)]
233        location: snafu::Location,
234    },
235
236    #[snafu(display("git-chronicle binary not found on PATH, at {location}"))]
237    BinaryNotFound {
238        #[snafu(implicit)]
239        location: snafu::Location,
240    },
241
242    #[snafu(display("failed to write {path}: {source}, at {location}"))]
243    WriteFile {
244        path: String,
245        #[snafu(source)]
246        source: std::io::Error,
247        #[snafu(implicit)]
248        location: snafu::Location,
249    },
250
251    #[snafu(display("failed to read file {path}: {source}, at {location}"))]
252    ReadFile {
253        path: String,
254        #[snafu(source)]
255        source: std::io::Error,
256        #[snafu(implicit)]
257        location: snafu::Location,
258    },
259
260    #[snafu(display("failed to read user config: {source}, at {location}"))]
261    ReadConfig {
262        #[snafu(source)]
263        source: toml::de::Error,
264        #[snafu(implicit)]
265        location: snafu::Location,
266    },
267
268    #[snafu(display("failed to write user config: {source}, at {location}"))]
269    WriteConfig {
270        #[snafu(source)]
271        source: toml::ser::Error,
272        #[snafu(implicit)]
273        location: snafu::Location,
274    },
275
276    #[snafu(display(
277        "Claude CLI not found — install Claude Code or select a different provider, at {location}"
278    ))]
279    ClaudeCliNotFound {
280        #[snafu(implicit)]
281        location: snafu::Location,
282    },
283
284    #[snafu(display("ANTHROPIC_API_KEY environment variable not set, at {location}"))]
285    ApiKeyNotSet {
286        #[snafu(implicit)]
287        location: snafu::Location,
288    },
289
290    #[snafu(display("interactive input error: {source}, at {location}"))]
291    InteractiveInput {
292        #[snafu(source)]
293        source: std::io::Error,
294        #[snafu(implicit)]
295        location: snafu::Location,
296    },
297}
298
299pub type Result<T, E = ChronicleError> = std::result::Result<T, E>;