scientific_workflow/runtime/
error.rs1use std::io;
4
5use thiserror::Error;
6
7use crate::configuration::ConfigurationError;
8
9#[derive(Debug, Error)]
11#[non_exhaustive]
12pub enum RuntimeError {
13 #[error("workflow phase execution failed: {source}")]
15 PhaseExecutionFailed {
16 summary: super::RuntimeSummary,
18 #[source]
20 source: Box<RuntimeError>,
21 },
22
23 #[error(transparent)]
25 Configuration(#[from] ConfigurationError),
26
27 #[error("another progress reporter already owns the process terminal")]
29 TerminalAlreadyOwned,
30
31 #[error("task count {task_count} exceeds this platform's addressable progress slots")]
33 TaskCountTooLarge {
34 task_count: u64,
36 },
37
38 #[error("phase {phase} must contain at least one task")]
40 EmptyPhase { phase: u64 },
41
42 #[error("at least one phase is required")]
44 EmptyPhaseSet,
45
46 #[error("phase {phase} must have a nonempty label")]
48 InvalidPhaseLabel { phase: u64 },
49
50 #[error("phase {phase} max_concurrent_workloads must be greater than zero")]
52 InvalidPhaseWorkloadLimit { phase: u64 },
53
54 #[error("phase {phase} queue_capacity must be greater than zero")]
56 InvalidPhaseQueueCapacity { phase: u64 },
57
58 #[error("phase ID {phase} appears more than once")]
60 DuplicatePhaseId { phase: u64 },
61
62 #[error("phase {phase} depends on unknown phase {dependency}")]
64 UnknownPhaseDependency { phase: u64, dependency: u64 },
65
66 #[error("phase dependency graph contains a cycle involving phase {phase}")]
68 PhaseDependencyCycle { phase: u64 },
69
70 #[error("selected phase {phase} is not registered")]
72 UnknownSelectedPhase { phase: u64 },
73
74 #[error("selected phase {phase} requires unsatisfied phase {dependency}")]
76 UnsatisfiedPhaseDependency { phase: u64, dependency: u64 },
77
78 #[error("confirmation input ended after phase {phase} before the next phase could start")]
80 PhaseConfirmationEof { phase: u64 },
81
82 #[error("failed to read confirmation after phase {phase}")]
84 PhaseConfirmationInput {
85 phase: u64,
87 #[source]
89 source: io::Error,
90 },
91
92 #[error("task `{task}` has no workload")]
94 MissingTaskWorkload { task: String },
95
96 #[error("task `{task}` failed: {source}")]
98 TaskWorkload {
99 task: String,
100 #[source]
101 source: Box<dyn std::error::Error + Send + Sync + 'static>,
102 },
103
104 #[error("a runtime scheduler worker panicked")]
106 SchedulerPanicked,
107
108 #[error("workflow runtime was cancelled")]
110 Cancelled,
111
112 #[error("phase {phase} contains an empty task ID")]
114 InvalidManagedTaskId { phase: u64 },
115
116 #[error("task `{task}` must have a nonempty kind")]
118 InvalidManagedTaskKind { task: String },
119
120 #[error("phase {phase} repeats task ID `{task}`")]
122 DuplicateManagedTaskId { phase: u64, task: String },
123
124 #[error("task kind `{kind}` has inconsistent parameter keys between `{first}` and `{second}`")]
126 InconsistentManagedTaskParameters {
127 kind: String,
128 first: String,
129 second: String,
130 },
131
132 #[error("task kind `{kind}` is not declared by the phase")]
134 UnknownManagedTaskKind { kind: String },
135
136 #[error("generated task label `{label}` collides between `{first}` and `{second}`")]
138 ManagedTaskDisplayCollision {
139 label: String,
140 first: String,
141 second: String,
142 },
143
144 #[error("task selector `{selector}` matched no task")]
146 ManagedTaskNotFound { selector: String },
147
148 #[error("task selector `{selector}` is ambiguous between `{first}` and `{second}`")]
150 ManagedTaskSelectorAmbiguous {
151 selector: String,
152 first: String,
153 second: String,
154 },
155
156 #[error("task `{task}` does not contain parameter `{key}`")]
158 UnknownManagedTaskParameter { task: String, key: String },
159
160 #[error("task `{task}` parameter `{key}` could not be decoded")]
162 DecodeManagedTaskParameter {
163 task: String,
164 key: String,
165 #[source]
166 source: serde_json::Error,
167 },
168
169 #[error("task parameter key `{key}` is invalid")]
171 InvalidTaskParameter { key: String },
172
173 #[error("configuration-derived task `{task}` has immutable parameters")]
175 ConfiguredTaskParametersImmutable { task: String },
176
177 #[error("managed task `{task}` does not exist in this reporter")]
179 UnknownManagedTask { task: String },
180
181 #[error("task `{task}` is declared as {actual}, not {requested}")]
183 ManagedTaskKindMismatch {
184 task: String,
185 requested: &'static str,
186 actual: &'static str,
187 },
188
189 #[error("task identity repeats parameter key `{key}`")]
191 DuplicateIdentityParameter {
192 key: String,
194 },
195
196 #[error("task identity parameter `{key}` is not declared by the project")]
198 UnknownIdentityParameter {
199 key: String,
201 },
202
203 #[error(
205 "task identity `{identity}` is shared by ordinals {first_ordinal} and {second_ordinal}"
206 )]
207 NonUniqueTaskIdentity {
208 identity: String,
210 first_ordinal: u64,
212 second_ordinal: u64,
214 },
215
216 #[error("task ordinal {task_ordinal} is outside the reporter's task registry")]
218 UnknownTaskOrdinal {
219 task_ordinal: u64,
221 },
222
223 #[error("registered task `{identity}` does not exist")]
225 UnknownRegisteredTask { identity: String },
226
227 #[error("registered task `{identity}` appears more than once")]
229 DuplicateRegisteredTask { identity: String },
230
231 #[error("task ordinal {task_ordinal} does not match its registered parameter identity")]
233 TaskIdentityMismatch {
234 task_ordinal: u64,
236 },
237
238 #[error("task `{identity}` has already started or reached a terminal status")]
240 TaskAlreadyStarted {
241 identity: String,
243 },
244
245 #[error("task `{identity}` starts at iteration {initial}, beyond target {target}")]
247 InitialIterationBeyondTarget {
248 identity: String,
250 initial: u64,
252 target: u64,
254 },
255
256 #[error("task `{identity}` cannot move progress from iteration {current} back to {attempted}")]
258 IterationRegressed {
259 identity: String,
261 current: u64,
263 attempted: u64,
265 },
266
267 #[error("task `{identity}` reported iteration {iteration}, beyond target {target}")]
269 IterationBeyondTarget {
270 identity: String,
272 iteration: u64,
274 target: u64,
276 },
277
278 #[error("task `{identity}` completed at iteration {current}, before target {target}")]
280 TargetIterationNotReached {
281 identity: String,
283 current: u64,
285 target: u64,
287 },
288
289 #[error("failed to start the centralized terminal reporter")]
291 StartRenderer {
292 #[source]
294 source: io::Error,
295 },
296
297 #[error("failed to {operation} for the isolated progress screen")]
299 TerminalSetup {
300 operation: &'static str,
301 #[source]
302 source: io::Error,
303 },
304
305 #[error("the centralized terminal reporter is no longer available")]
307 RendererUnavailable,
308
309 #[error("the centralized terminal reporter panicked")]
311 RendererPanicked,
312
313 #[error(
315 "cannot report success with {pending} pending, {running} running, and {failed} failed tasks"
316 )]
317 IncompleteProgress {
318 pending: u64,
320 running: u64,
322 failed: u64,
324 },
325}
326
327impl RuntimeError {
328 pub fn runtime_summary(&self) -> Option<&super::RuntimeSummary> {
330 match self {
331 Self::PhaseExecutionFailed { summary, .. } => Some(summary),
332 _ => None,
333 }
334 }
335
336 pub fn execution_cause(&self) -> Option<&RuntimeError> {
338 match self {
339 Self::PhaseExecutionFailed { source, .. } => Some(source),
340 _ => None,
341 }
342 }
343}
344
345pub(crate) use RuntimeError as ReportingError;