1use std::fmt;
2
3use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, FlowError>;
7
8#[derive(Error)]
10pub enum FlowError {
11 #[error("workflow run not found: {0}")]
12 RunNotFound(String),
13
14 #[error("workflow run {0} is already terminal")]
15 RunTerminal(String),
16
17 #[error("workflow run id is invalid: {0}")]
18 InvalidRunId(String),
19
20 #[error("workflow run {run_id} conflicts with existing run: {reason}")]
21 RunConflict { run_id: String, reason: String },
22
23 #[error("non-deterministic workflow replay for run {run_id}: {reason}")]
24 NonDeterministic { run_id: String, reason: String },
25
26 #[error(
27 "event sequence conflict for run {run_id}: expected {expected_sequence}, actual {actual_sequence}"
28 )]
29 EventConflict {
30 run_id: String,
31 expected_sequence: u64,
32 actual_sequence: u64,
33 },
34
35 #[error("active hook token not found (value redacted)")]
38 HookTokenNotFound(String),
39
40 #[error("workflow task lease is no longer active: {0}")]
41 LeaseLost(String),
42
43 #[error(
46 "active hook token is already used by run {existing_run_id} hook {existing_hook_id} (value redacted)"
47 )]
48 HookTokenConflict {
49 token: String,
50 existing_run_id: String,
51 existing_hook_id: String,
52 },
53
54 #[error("invalid workflow definition: {0}")]
55 InvalidWorkflow(String),
56
57 #[error("invalid state transition: {0}")]
58 InvalidTransition(String),
59
60 #[error("invalid worker configuration: {0}")]
61 InvalidWorkerConfiguration(String),
62
63 #[error("task manager error: {0}")]
64 TaskManagement(String),
65
66 #[error("event store error: {0}")]
67 Store(String),
68
69 #[error("runtime error: {0}")]
70 Runtime(String),
71
72 #[error("serialization error: {0}")]
73 Serialization(#[from] serde_json::Error),
74
75 #[error("io error: {0}")]
76 Io(#[from] std::io::Error),
77
78 #[error("workflow replay exceeded {0} iterations")]
79 ReplayLimitExceeded(usize),
80}
81
82impl fmt::Debug for FlowError {
86 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
87 match self {
88 Self::RunNotFound(run_id) => {
89 formatter.debug_tuple("RunNotFound").field(run_id).finish()
90 }
91 Self::RunTerminal(run_id) => {
92 formatter.debug_tuple("RunTerminal").field(run_id).finish()
93 }
94 Self::InvalidRunId(run_id) => {
95 formatter.debug_tuple("InvalidRunId").field(run_id).finish()
96 }
97 Self::RunConflict { run_id, reason } => formatter
98 .debug_struct("RunConflict")
99 .field("run_id", run_id)
100 .field("reason", reason)
101 .finish(),
102 Self::NonDeterministic { run_id, reason } => formatter
103 .debug_struct("NonDeterministic")
104 .field("run_id", run_id)
105 .field("reason", reason)
106 .finish(),
107 Self::EventConflict {
108 run_id,
109 expected_sequence,
110 actual_sequence,
111 } => formatter
112 .debug_struct("EventConflict")
113 .field("run_id", run_id)
114 .field("expected_sequence", expected_sequence)
115 .field("actual_sequence", actual_sequence)
116 .finish(),
117 Self::HookTokenNotFound(_) => formatter
118 .debug_tuple("HookTokenNotFound")
119 .field(&"<redacted>")
120 .finish(),
121 Self::LeaseLost(lease_id) => {
122 formatter.debug_tuple("LeaseLost").field(lease_id).finish()
123 }
124 Self::HookTokenConflict {
125 existing_run_id,
126 existing_hook_id,
127 ..
128 } => formatter
129 .debug_struct("HookTokenConflict")
130 .field("token", &"<redacted>")
131 .field("existing_run_id", existing_run_id)
132 .field("existing_hook_id", existing_hook_id)
133 .finish(),
134 Self::InvalidWorkflow(message) => formatter
135 .debug_tuple("InvalidWorkflow")
136 .field(message)
137 .finish(),
138 Self::InvalidTransition(message) => formatter
139 .debug_tuple("InvalidTransition")
140 .field(message)
141 .finish(),
142 Self::InvalidWorkerConfiguration(message) => formatter
143 .debug_tuple("InvalidWorkerConfiguration")
144 .field(message)
145 .finish(),
146 Self::TaskManagement(message) => formatter
147 .debug_tuple("TaskManagement")
148 .field(message)
149 .finish(),
150 Self::Store(message) => formatter.debug_tuple("Store").field(message).finish(),
151 Self::Runtime(message) => formatter.debug_tuple("Runtime").field(message).finish(),
152 Self::Serialization(error) => {
153 formatter.debug_tuple("Serialization").field(error).finish()
154 }
155 Self::Io(error) => formatter.debug_tuple("Io").field(error).finish(),
156 Self::ReplayLimitExceeded(limit) => formatter
157 .debug_tuple("ReplayLimitExceeded")
158 .field(limit)
159 .finish(),
160 }
161 }
162}