Skip to main content

taquba_workflow/
error.rs

1use thiserror::Error;
2
3/// Errors returned by the runtime's submission and worker paths.
4#[derive(Debug, Error)]
5#[non_exhaustive]
6pub enum Error {
7    /// A step job is missing the [`crate::HEADER_RUN_ID`] header.
8    /// Permanent: a misconfigured job will not become valid on retry.
9    #[error("step job is missing header `{0}`")]
10    MissingHeader(&'static str),
11
12    /// A step job's [`crate::HEADER_STEP`] header is not a valid `u32`.
13    /// Permanent: header value won't change across retries.
14    #[error("step job has invalid `{header}` header `{value}`")]
15    InvalidStepHeader {
16        /// Header name.
17        header: &'static str,
18        /// Offending value.
19        value: String,
20    },
21
22    /// A submission included a user header starting with the reserved
23    /// `workflow.*` prefix. The runtime owns that prefix; submitters must use
24    /// any other key.
25    #[error("submission header `{0}` uses the reserved `workflow.*` prefix")]
26    ReservedHeaderInSubmit(String),
27
28    /// A re-submission of an active `run_id` carried `spec.input` bytes
29    /// that differ from the original submission's. Reusing a `run_id`
30    /// with new input is treated as a programmer error: pick a fresh
31    /// `run_id` for a new run, or wait for the active one to terminate.
32    #[error("run `{0}` is active with a different input; pick a fresh run_id")]
33    InputMismatch(String),
34
35    /// Underlying error from a Taquba queue operation.
36    #[error(transparent)]
37    Queue(#[from] taquba::Error),
38}
39
40impl Error {
41    /// True if retrying the operation will not change the outcome; callers
42    /// should fast-fail (e.g. dead-letter a step, mark a submission as
43    /// failed) rather than back off and try again.
44    ///
45    /// [`Self::Queue`] delegates to [`taquba::Error::is_permanent`].
46    pub fn is_permanent(&self) -> bool {
47        match self {
48            Self::MissingHeader(_)
49            | Self::InvalidStepHeader { .. }
50            | Self::ReservedHeaderInSubmit(_)
51            | Self::InputMismatch(_) => true,
52            Self::Queue(e) => e.is_permanent(),
53        }
54    }
55}
56
57/// Result alias used throughout the crate.
58pub type Result<T> = std::result::Result<T, Error>;
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn workflow_variants_are_permanent() {
66        assert!(Error::MissingHeader("workflow.run_id").is_permanent());
67        assert!(
68            Error::InvalidStepHeader {
69                header: "workflow.step",
70                value: "not-a-u32".into(),
71            }
72            .is_permanent()
73        );
74        assert!(Error::ReservedHeaderInSubmit("workflow.foo".into()).is_permanent());
75        assert!(Error::InputMismatch("run-1".into()).is_permanent());
76    }
77
78    #[test]
79    fn queue_classifies_per_inner_variant() {
80        assert!(Error::Queue(taquba::Error::JobNotFound("job-1".into())).is_permanent());
81        assert!(Error::Queue(taquba::Error::InvalidState).is_permanent());
82        assert!(Error::Queue(taquba::Error::KvValueTooLarge { size: 10, max: 5 }).is_permanent());
83    }
84}