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("hook {hook_id} for workflow run {run_id} conflicts with request: {reason}")]
55 HookConflict {
56 run_id: String,
57 hook_id: String,
58 reason: String,
59 },
60
61 #[error("invalid workflow definition: {0}")]
62 InvalidWorkflow(String),
63
64 #[error("invalid state transition: {0}")]
65 InvalidTransition(String),
66
67 #[error("invalid worker configuration: {0}")]
68 InvalidWorkerConfiguration(String),
69
70 #[error("task manager error: {0}")]
71 TaskManagement(String),
72
73 #[error("event store error: {0}")]
74 Store(String),
75
76 #[error("runtime error: {0}")]
77 Runtime(String),
78
79 #[error("serialization error: {0}")]
80 Serialization(#[from] serde_json::Error),
81
82 #[error("io error: {0}")]
83 Io(#[from] std::io::Error),
84
85 #[error("workflow replay exceeded {0} iterations")]
86 ReplayLimitExceeded(usize),
87}
88
89impl fmt::Debug for FlowError {
93 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
94 match self {
95 Self::RunNotFound(run_id) => {
96 formatter.debug_tuple("RunNotFound").field(run_id).finish()
97 }
98 Self::RunTerminal(run_id) => {
99 formatter.debug_tuple("RunTerminal").field(run_id).finish()
100 }
101 Self::InvalidRunId(run_id) => {
102 formatter.debug_tuple("InvalidRunId").field(run_id).finish()
103 }
104 Self::RunConflict { run_id, reason } => formatter
105 .debug_struct("RunConflict")
106 .field("run_id", run_id)
107 .field("reason", reason)
108 .finish(),
109 Self::NonDeterministic { run_id, reason } => formatter
110 .debug_struct("NonDeterministic")
111 .field("run_id", run_id)
112 .field("reason", reason)
113 .finish(),
114 Self::EventConflict {
115 run_id,
116 expected_sequence,
117 actual_sequence,
118 } => formatter
119 .debug_struct("EventConflict")
120 .field("run_id", run_id)
121 .field("expected_sequence", expected_sequence)
122 .field("actual_sequence", actual_sequence)
123 .finish(),
124 Self::HookTokenNotFound(_) => formatter
125 .debug_tuple("HookTokenNotFound")
126 .field(&"<redacted>")
127 .finish(),
128 Self::LeaseLost(lease_id) => {
129 formatter.debug_tuple("LeaseLost").field(lease_id).finish()
130 }
131 Self::HookTokenConflict {
132 existing_run_id,
133 existing_hook_id,
134 ..
135 } => formatter
136 .debug_struct("HookTokenConflict")
137 .field("token", &"<redacted>")
138 .field("existing_run_id", existing_run_id)
139 .field("existing_hook_id", existing_hook_id)
140 .finish(),
141 Self::HookConflict {
142 run_id,
143 hook_id,
144 reason,
145 } => formatter
146 .debug_struct("HookConflict")
147 .field("run_id", run_id)
148 .field("hook_id", hook_id)
149 .field("reason", reason)
150 .finish(),
151 Self::InvalidWorkflow(message) => formatter
152 .debug_tuple("InvalidWorkflow")
153 .field(message)
154 .finish(),
155 Self::InvalidTransition(message) => formatter
156 .debug_tuple("InvalidTransition")
157 .field(message)
158 .finish(),
159 Self::InvalidWorkerConfiguration(message) => formatter
160 .debug_tuple("InvalidWorkerConfiguration")
161 .field(message)
162 .finish(),
163 Self::TaskManagement(message) => formatter
164 .debug_tuple("TaskManagement")
165 .field(message)
166 .finish(),
167 Self::Store(message) => formatter.debug_tuple("Store").field(message).finish(),
168 Self::Runtime(message) => formatter.debug_tuple("Runtime").field(message).finish(),
169 Self::Serialization(error) => {
170 formatter.debug_tuple("Serialization").field(error).finish()
171 }
172 Self::Io(error) => formatter.debug_tuple("Io").field(error).finish(),
173 Self::ReplayLimitExceeded(limit) => formatter
174 .debug_tuple("ReplayLimitExceeded")
175 .field(limit)
176 .finish(),
177 }
178 }
179}