1use crate::schedule::{ScheduleError, ScheduleEvaluatorError};
4use aion_core::{RunId, ScheduleId, WorkflowId};
5use aion_package::{ContentHash, PackageError};
6use aion_store::StoreError;
7
8use crate::durability::DurabilityError;
9
10#[derive(thiserror::Error, Debug)]
12pub enum EngineError {
13 #[error("engine store is required")]
15 MissingStore,
16
17 #[error(
19 "engine visibility store is required; call EngineBuilder::visibility_store() or EngineBuilder::in_memory_visibility()"
20 )]
21 MissingVisibilityStore,
22
23 #[error("workflow package load failed: {reason}")]
25 Load {
26 reason: String,
28 },
29
30 #[error(
32 "workflow `{workflow_type}` version `{version}` is not loaded (loaded versions: {loaded})"
33 )]
34 UnknownVersion {
35 workflow_type: String,
37 version: ContentHash,
39 loaded: String,
41 },
42
43 #[error("cannot unload workflow `{workflow_type}` version `{version}`: {pinned_by}")]
45 VersionPinned {
46 workflow_type: String,
48 version: ContentHash,
50 pinned_by: PinHolder,
52 },
53
54 #[error(
56 "cannot unload workflow `{workflow_type}` version `{version}`: it is the route-active version; route another version first"
57 )]
58 RouteActive {
59 workflow_type: String,
61 version: ContentHash,
63 },
64
65 #[error(
70 "workflow `{workflow_type}` version `{version}` is already loaded with a different manifest (resident digest {resident_digest}, incoming digest {incoming_digest}); the content hash covers beams only — rebuild the archive so its manifest matches the resident version, or change the beam set"
71 )]
72 ManifestMismatch {
73 workflow_type: String,
75 version: ContentHash,
77 resident_digest: String,
79 incoming_digest: String,
81 },
82
83 #[error(
85 "conflicting event publisher configuration: EngineBuilder::event_streaming installs the broadcast publisher and cannot be combined with EngineBuilder::event_publisher"
86 )]
87 ConflictingEventPublisher,
88
89 #[error("event streaming setup failed: {0}")]
91 EventStreaming(#[from] crate::publish::PublishError),
92
93 #[error("store error: {0}")]
95 Store(#[from] StoreError),
96
97 #[error("durability error: {0}")]
99 Durability(#[from] DurabilityError),
100
101 #[error("package error: {0}")]
103 Package(#[from] PackageError),
104
105 #[error("runtime error: {reason}")]
107 Runtime {
108 reason: String,
110 },
111
112 #[error("active workflow registry lock was poisoned")]
114 RegistryPoisoned,
115
116 #[error("workflow catalog lock was poisoned")]
118 CatalogPoisoned,
119
120 #[error("engine is shutting down")]
122 ShuttingDown,
123
124 #[error("workflow `{workflow_type}` was not found")]
126 WorkflowNotFound {
127 workflow_type: String,
129 },
130
131 #[error("schedule `{schedule_id}` was not found")]
133 ScheduleNotFound {
134 schedule_id: ScheduleId,
136 },
137
138 #[error("schedule error: {reason}")]
140 Schedule {
141 reason: String,
143 },
144
145 #[error("NIF registration failed: {reason}")]
147 NifRegistration {
148 reason: String,
150 },
151
152 #[error("signal router error: {0}")]
154 SignalRouter(#[from] SignalRouterError),
155
156 #[error("query error: {0}")]
158 Query(#[from] crate::query::QueryError),
159}
160
161#[derive(Debug, Clone, PartialEq, Eq)]
163pub enum PinHolder {
164 InFlightStart,
166 LiveRun {
168 workflow_id: WorkflowId,
170 run_id: RunId,
172 },
173 RecoverableRun {
175 workflow_id: WorkflowId,
177 },
178 RecordedChild {
180 child_workflow_id: WorkflowId,
182 recorded_by: WorkflowId,
184 },
185}
186
187impl std::fmt::Display for PinHolder {
188 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189 match self {
190 Self::InFlightStart => formatter.write_str("an in-flight start is pinned to it"),
191 Self::LiveRun {
192 workflow_id,
193 run_id,
194 } => write!(
195 formatter,
196 "live run `{workflow_id}/{run_id}` is pinned to it"
197 ),
198 Self::RecoverableRun { workflow_id } => {
199 write!(formatter, "recoverable run `{workflow_id}` is pinned to it")
200 }
201 Self::RecordedChild {
202 child_workflow_id,
203 recorded_by,
204 } => write!(
205 formatter,
206 "child `{child_workflow_id}` recorded by `{recorded_by}` is pinned to it and has not started"
207 ),
208 }
209 }
210}
211
212#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
214pub enum SignalRouterError {
215 #[error("workflow {workflow_id}/{run_id} is terminal")]
217 Terminal {
218 workflow_id: WorkflowId,
220 run_id: RunId,
222 },
223
224 #[error("signal resume handoff failed: {reason}")]
226 Handoff {
227 reason: String,
229 },
230
231 #[error(
233 "signal `{signal_name}` for workflow {workflow_id}/{run_id} could not be delivered to process {process_id}: {reason}"
234 )]
235 DeliveryFailed {
236 workflow_id: WorkflowId,
238 run_id: RunId,
240 process_id: u64,
242 signal_name: String,
244 reason: String,
246 },
247}
248
249impl From<ScheduleError> for EngineError {
250 fn from(error: ScheduleError) -> Self {
251 Self::Schedule {
252 reason: error.to_string(),
253 }
254 }
255}
256
257impl From<ScheduleEvaluatorError> for EngineError {
258 fn from(error: ScheduleEvaluatorError) -> Self {
259 match error {
260 ScheduleEvaluatorError::ScheduleNotFound { schedule_id } => {
261 Self::ScheduleNotFound { schedule_id }
262 }
263 other => Self::Schedule {
264 reason: other.to_string(),
265 },
266 }
267 }
268}