scientific_workflow/runtime/
error.rs1use std::io;
4use std::path::PathBuf;
5use std::time::Duration;
6
7use thiserror::Error;
8
9use crate::configuration::ConfigurationError;
10
11#[derive(Debug, Error)]
13#[non_exhaustive]
14pub enum RuntimeError {
15 #[error("failed to serialize workflow execution record")]
17 SerializeExecutionRecord {
18 #[source]
19 source: serde_json::Error,
20 },
21
22 #[error("failed to write workflow execution record `{path}`")]
24 WriteExecutionRecord {
25 path: PathBuf,
26 #[source]
27 source: io::Error,
28 },
29
30 #[error("failed to format UTC timestamp while attempting to {operation}")]
32 ExecutionRecordTimestamp {
33 operation: &'static str,
34 #[source]
35 source: time::error::Format,
36 },
37
38 #[error("failed to serialize workflow execution plan")]
40 SerializeExecutionPlan {
41 #[source]
42 source: serde_json::Error,
43 },
44
45 #[error("failed to write workflow execution plan `{path}`")]
47 WriteExecutionPlan {
48 path: PathBuf,
49 #[source]
50 source: io::Error,
51 },
52
53 #[error("workflow execution plan destination `{path}` already contains different data")]
55 ExecutionPlanConflict { path: PathBuf },
56
57 #[error("workflow phase execution failed: {source}")]
59 PhaseExecutionFailed {
60 summary: super::RuntimeSummary,
62 #[source]
64 source: Box<RuntimeError>,
65 },
66
67 #[error(transparent)]
69 Configuration(#[from] ConfigurationError),
70
71 #[error("another progress reporter already owns the process terminal")]
73 TerminalAlreadyOwned,
74
75 #[error("task count {task_count} exceeds this platform's addressable progress slots")]
77 TaskCountTooLarge {
78 task_count: u64,
80 },
81
82 #[error("phase {phase} must contain at least one task")]
84 EmptyPhase { phase: u64 },
85
86 #[error("at least one phase is required")]
88 EmptyPhaseSet,
89
90 #[error("phase {phase} must have a nonempty label")]
92 InvalidPhaseLabel { phase: u64 },
93
94 #[error("phase {phase} max_active_tasks must be greater than zero")]
96 InvalidPhaseWorkloadLimit { phase: u64 },
97
98 #[error("phase {phase} prepared_task_queue_capacity must be greater than zero")]
100 InvalidPhaseQueueCapacity { phase: u64 },
101
102 #[error("phase {phase} timing setting `{setting}` must be nonzero and representable")]
104 InvalidPhaseTiming { phase: u64, setting: &'static str },
105
106 #[error("phase ID {phase} appears more than once")]
108 DuplicatePhaseId { phase: u64 },
109
110 #[error("phase {phase} depends on unknown phase {dependency}")]
112 UnknownPhaseDependency { phase: u64, dependency: u64 },
113
114 #[error("phase dependency graph contains a cycle involving phase {phase}")]
116 PhaseDependencyCycle { phase: u64 },
117
118 #[error("selected phase {phase} is not registered")]
120 UnknownSelectedPhase { phase: u64 },
121
122 #[error("selected phase {phase} requires unsatisfied phase {dependency}")]
124 UnsatisfiedPhaseDependency { phase: u64, dependency: u64 },
125
126 #[error("confirmation input ended after phase {phase} before the next phase could start")]
128 PhaseConfirmationEof { phase: u64 },
129
130 #[error("failed to read confirmation after phase {phase}")]
132 PhaseConfirmationInput {
133 phase: u64,
135 #[source]
137 source: io::Error,
138 },
139
140 #[error("task `{task}` has no workload")]
142 MissingTaskWorkload { task: String },
143
144 #[error("task `{task}` failed: {source}")]
146 TaskWorkload {
147 task: String,
148 #[source]
149 source: Box<dyn std::error::Error + Send + Sync + 'static>,
150 },
151
152 #[error("task `{task}` exceeded its timeout of {timeout:?}")]
154 TaskTimedOut { task: String, timeout: Duration },
155
156 #[error("phase {phase} exceeded its deadline of {deadline_after:?}")]
158 PhaseDeadlineExceeded {
159 phase: u64,
160 deadline_after: Duration,
161 },
162
163 #[error("a runtime scheduler worker panicked")]
165 SchedulerPanicked,
166
167 #[error("workflow runtime was cancelled")]
169 Cancelled,
170
171 #[error("phase {phase} contains an empty task ID")]
173 InvalidManagedTaskId { phase: u64 },
174
175 #[error("task `{task}` must have a nonempty kind")]
177 InvalidManagedTaskKind { task: String },
178
179 #[error("phase {phase} repeats task ID `{task}`")]
181 DuplicateManagedTaskId { phase: u64, task: String },
182
183 #[error("task kind `{kind}` has inconsistent parameter keys between `{first}` and `{second}`")]
185 InconsistentManagedTaskParameters {
186 kind: String,
187 first: String,
188 second: String,
189 },
190
191 #[error("task kind `{kind}` is not declared by the phase")]
193 UnknownManagedTaskKind { kind: String },
194
195 #[error("generated task label `{label}` collides between `{first}` and `{second}`")]
197 ManagedTaskDisplayCollision {
198 label: String,
199 first: String,
200 second: String,
201 },
202
203 #[error("task selector `{selector}` matched no task")]
205 ManagedTaskNotFound { selector: String },
206
207 #[error("task selector `{selector}` is ambiguous between `{first}` and `{second}`")]
209 ManagedTaskSelectorAmbiguous {
210 selector: String,
211 first: String,
212 second: String,
213 },
214
215 #[error("task `{task}` does not contain parameter `{key}`")]
217 UnknownManagedTaskParameter { task: String, key: String },
218
219 #[error("task `{task}` parameter `{key}` could not be decoded")]
221 DecodeManagedTaskParameter {
222 task: String,
223 key: String,
224 #[source]
225 source: serde_json::Error,
226 },
227
228 #[error("task parameter key `{key}` is invalid")]
230 InvalidTaskParameter { key: String },
231
232 #[error("configuration-derived task `{task}` has immutable parameters")]
234 ConfiguredTaskParametersImmutable { task: String },
235
236 #[error("managed task `{task}` does not exist in this reporter")]
238 UnknownManagedTask { task: String },
239
240 #[error("task `{task}` is declared as {actual}, not {requested}")]
242 ManagedTaskKindMismatch {
243 task: String,
244 requested: &'static str,
245 actual: &'static str,
246 },
247
248 #[error("task identity repeats parameter key `{key}`")]
250 DuplicateIdentityParameter {
251 key: String,
253 },
254
255 #[error("task identity parameter `{key}` is not declared by the project")]
257 UnknownIdentityParameter {
258 key: String,
260 },
261
262 #[error(
264 "task identity `{identity}` is shared by ordinals {first_ordinal} and {second_ordinal}"
265 )]
266 NonUniqueTaskIdentity {
267 identity: String,
269 first_ordinal: u64,
271 second_ordinal: u64,
273 },
274
275 #[error("task ordinal {task_ordinal} is outside the reporter's task registry")]
277 UnknownTaskOrdinal {
278 task_ordinal: u64,
280 },
281
282 #[error("registered task `{identity}` does not exist")]
284 UnknownRegisteredTask { identity: String },
285
286 #[error("registered task `{identity}` appears more than once")]
288 DuplicateRegisteredTask { identity: String },
289
290 #[error("task ordinal {task_ordinal} does not match its registered parameter identity")]
292 TaskIdentityMismatch {
293 task_ordinal: u64,
295 },
296
297 #[error("task `{identity}` has already started or reached a terminal status")]
299 TaskAlreadyStarted {
300 identity: String,
302 },
303
304 #[error("task `{identity}` starts at iteration {initial}, beyond target {target}")]
306 InitialIterationBeyondTarget {
307 identity: String,
309 initial: u64,
311 target: u64,
313 },
314
315 #[error("task `{identity}` cannot move progress from iteration {current} back to {attempted}")]
317 IterationRegressed {
318 identity: String,
320 current: u64,
322 attempted: u64,
324 },
325
326 #[error("task `{identity}` reported iteration {iteration}, beyond target {target}")]
328 IterationBeyondTarget {
329 identity: String,
331 iteration: u64,
333 target: u64,
335 },
336
337 #[error("task `{identity}` completed at iteration {current}, before target {target}")]
339 TargetIterationNotReached {
340 identity: String,
342 current: u64,
344 target: u64,
346 },
347
348 #[error("failed to start the centralized terminal reporter")]
350 StartRenderer {
351 #[source]
353 source: io::Error,
354 },
355
356 #[error("failed to {operation} for the isolated progress screen")]
358 TerminalSetup {
359 operation: &'static str,
360 #[source]
361 source: io::Error,
362 },
363
364 #[error("the centralized terminal reporter is no longer available")]
366 RendererUnavailable,
367
368 #[error("the centralized terminal reporter panicked")]
370 RendererPanicked,
371
372 #[error(
374 "cannot report success with {pending} pending, {running} running, and {failed} failed tasks"
375 )]
376 IncompleteProgress {
377 pending: u64,
379 running: u64,
381 failed: u64,
383 },
384}
385
386impl RuntimeError {
387 pub fn runtime_summary(&self) -> Option<&super::RuntimeSummary> {
389 match self {
390 Self::PhaseExecutionFailed { summary, .. } => Some(summary),
391 _ => None,
392 }
393 }
394
395 pub fn execution_cause(&self) -> Option<&RuntimeError> {
397 match self {
398 Self::PhaseExecutionFailed { source, .. } => Some(source),
399 _ => None,
400 }
401 }
402}
403
404pub(crate) use RuntimeError as ReportingError;