agent_effects/error.rs
1//! Infrastructure errors.
2//!
3//! These are failures of the runtime itself: storage, configuration,
4//! serialization. What happened to an effect, including "unknown", is an
5//! [`EffectOutcome`](crate::EffectOutcome), never a `RuntimeError`.
6
7use crate::id::{EffectId, IdentityError};
8use crate::kind::EffectKind;
9use crate::store::StoreError;
10
11/// Why the runtime could not process an effect.
12#[derive(Debug, thiserror::Error)]
13#[non_exhaustive]
14pub enum RuntimeError {
15 /// The effect committed externally but no replayable output was stored.
16 /// This does not mean the external action failed.
17 #[error("effect {id} committed, but its output is unavailable for replay")]
18 OutputUnavailable {
19 /// The committed effect.
20 id: EffectId,
21 },
22
23 /// An older committed output needs an unsupported or failing migration.
24 #[error("effect {id} committed, but its output schema cannot be read: {source}")]
25 OutputPayload {
26 /// The committed effect.
27 id: EffectId,
28 /// The schema or migration problem.
29 #[source]
30 source: crate::PayloadError,
31 },
32
33 /// Recovery cannot safely interpret the stored input schema.
34 #[error("effect {id}'s input schema cannot be read: {source}")]
35 InputPayload {
36 /// The existing effect.
37 id: EffectId,
38 /// The schema or migration problem.
39 #[source]
40 source: crate::PayloadError,
41 },
42
43 /// A caller supplied a different input schema for an existing key.
44 #[error("effect {id} was recorded with input schema {stored}, not {requested}")]
45 InputSchemaMismatch {
46 /// The existing effect.
47 id: EffectId,
48 /// The recorded schema.
49 stored: u32,
50 /// The caller's schema.
51 requested: u32,
52 },
53 /// Existing safety assumptions differ from this call. Nothing is run
54 /// or replayed; the authoritative record remains unchanged.
55 #[error("effect {id} has an incompatible execution contract")]
56 ContractMismatch {
57 /// The existing effect.
58 id: EffectId,
59 },
60
61 /// A pre-contract unresolved record needs explicit operator review.
62 /// Its original idempotency and verification assumptions are unknown.
63 #[error("effect {id} has no execution contract; reconcile the legacy effect before resuming")]
64 LegacyContract {
65 /// The existing effect.
66 id: EffectId,
67 },
68 /// The effect name or logical key was invalid.
69 #[error("invalid effect identity: {0}")]
70 Identity(#[from] IdentityError),
71
72 /// The key was used before with a different input. Returning the earlier
73 /// effect's result would answer a question nobody asked.
74 #[error("effect {id} was recorded with a different input")]
75 InputMismatch {
76 /// The existing effect.
77 id: EffectId,
78 },
79
80 /// The key was used before with a different [`EffectKind`].
81 #[error("effect {id} was recorded as {stored:?}, not {requested:?}")]
82 KindMismatch {
83 /// The existing effect.
84 id: EffectId,
85 /// The kind on record.
86 stored: EffectKind,
87 /// The kind of this call.
88 requested: EffectKind,
89 },
90
91 /// The input could not be serialized.
92 #[error("could not serialize the effect input: {0}")]
93 Input(#[source] serde_json::Error),
94
95 /// A committed effect's stored output does not deserialize into the
96 /// requested type, e.g. because the type changed since it was recorded.
97 #[error(
98 "effect {id} committed, but its stored output does not match the requested type: {source}"
99 )]
100 Output {
101 /// The committed effect.
102 id: EffectId,
103 /// The deserialization error.
104 #[source]
105 source: serde_json::Error,
106 },
107
108 /// The runtime's risk policy requires something the effect lacks, so
109 /// it was refused before anything was recorded.
110 #[error("effect `{key}` violates the risk policy: it requires {requirement}")]
111 PolicyViolation {
112 /// The effect's `name:key`.
113 key: String,
114 /// What the policy requires, e.g. `verification`.
115 requirement: &'static str,
116 },
117
118 /// No effect has this name and key.
119 #[error("no effect `{key}` exists")]
120 NoSuchEffect {
121 /// The effect's `name:key`.
122 key: String,
123 },
124
125 /// The handler is registered without `.compensable()`, so its effects
126 /// cannot be compensated.
127 #[error("the handler for effect `{name}` is not registered as compensable")]
128 NotCompensable {
129 /// The effect name.
130 name: &'static str,
131 },
132
133 /// No handler of the submitted type is registered under its name.
134 #[error("no handler registered for effect `{name}`")]
135 NotRegistered {
136 /// The effect name.
137 name: &'static str,
138 },
139
140 /// A stored input no longer deserializes into the handler's input type,
141 /// e.g. because the type changed since the effect was recorded.
142 #[error("effect {id}'s stored input does not match its handler's input type: {source}")]
143 StoredInput {
144 /// The effect.
145 id: EffectId,
146 /// The deserialization error.
147 #[source]
148 source: serde_json::Error,
149 },
150
151 /// The store failed. If this happens after the action ran, the effect
152 /// stays in doubt and recovery will treat its outcome as unknown.
153 #[error(transparent)]
154 Store(#[from] StoreError),
155
156 /// The runtime's own task failed (a bug in `agent-effects`).
157 #[error("effect task failed: {0}")]
158 Internal(String),
159}