runifold_core/error.rs
1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use thiserror::Error;
6
7/// Whether retrying an operation is safe.
8#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
9#[non_exhaustive]
10pub enum RetrySafety {
11 /// The same operation can be retried safely.
12 Safe,
13 /// Retry is safe only when the external system honors an idempotency key.
14 RequiresIdempotency,
15 /// Visible output has been emitted, so retry may duplicate output.
16 UnsafeAfterVisibleOutput,
17 /// An external side effect may already have occurred.
18 UnsafeAfterSideEffect,
19 /// Safety cannot be determined.
20 Unknown,
21}
22
23/// A normalized run failure category.
24#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
25#[non_exhaustive]
26pub enum RunErrorKind {
27 /// Input or configuration is invalid.
28 InvalidInput,
29 /// A required capability was not granted.
30 CapabilityDenied,
31 /// A resource budget was exhausted.
32 BudgetExceeded,
33 /// A deadline elapsed.
34 DeadlineExceeded,
35 /// The run was cancelled.
36 Cancelled,
37 /// A transport failed.
38 Transport,
39 /// A remote protocol was malformed or violated its contract.
40 Protocol,
41 /// An invoked component failed.
42 Invocation,
43 /// A namespaced extension error.
44 Extension(String),
45}
46
47/// A structured run error suitable for policy decisions.
48#[derive(Clone, Debug, Deserialize, Error, PartialEq, Serialize)]
49#[error("{kind:?}: {message}")]
50pub struct RunError {
51 /// Normalized error category.
52 pub kind: RunErrorKind,
53 /// Safe human-readable explanation.
54 pub message: String,
55 /// Retry-safety classification.
56 pub retry_safety: RetrySafety,
57 /// Namespaced diagnostic metadata.
58 pub metadata: BTreeMap<String, Value>,
59}