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