Skip to main content

reifydb_sub_flow/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use reifydb_core::error::diagnostic::flow::{
5	flow_coordinator_busy, flow_coordinator_stopped, flow_ffi_unsupported_on_wasm, flow_invalid_worker_id,
6	flow_missing_input_edge, flow_node_input_arity, flow_parent_operator_not_found, flow_pool_actor_stopped,
7	flow_pool_busy, flow_sink_dictionary_not_found, flow_sink_missing_system_column,
8	flow_sink_view_not_visible_at_registration, flow_state_decode_failed, flow_state_encode_failed,
9	flow_transaction_dictionary_write_divergence, flow_transaction_keyspace_overlap, flow_unknown_diff_origin,
10	flow_unknown_operator, flow_unsupported_node, flow_worker_failed, flow_worker_stopped, native_abi_tag_mismatch,
11	native_create_failed, native_library_not_loaded, native_operator_not_found, native_symbol_not_found,
12};
13use reifydb_value::error::{Diagnostic, Error, IntoDiagnostic};
14
15#[derive(Debug, thiserror::Error)]
16pub enum FlowDispatchError {
17	#[error("invalid flow worker id {worker_id} (pool has {num_workers} workers)")]
18	InvalidWorkerId {
19		worker_id: usize,
20		num_workers: usize,
21	},
22
23	#[error("flow worker {worker_id} has stopped")]
24	WorkerStopped {
25		worker_id: usize,
26	},
27
28	#[error("flow pool is busy")]
29	PoolBusy,
30
31	#[error("flow coordinator is busy")]
32	CoordinatorBusy,
33
34	#[error("flow coordinator actor has stopped")]
35	CoordinatorStopped,
36
37	#[error("flow pool actor has stopped")]
38	PoolActorStopped,
39
40	#[error("flow transaction keyspace overlap: {key}")]
41	KeyspaceOverlap {
42		key: String,
43	},
44
45	#[error("dictionary write divergence on key {key}")]
46	DictionaryWriteDivergence {
47		key: String,
48	},
49
50	#[error("flow worker {worker_id} failed")]
51	WorkerFailed {
52		worker_id: usize,
53		cause: Error,
54	},
55}
56
57impl IntoDiagnostic for FlowDispatchError {
58	fn into_diagnostic(self) -> Diagnostic {
59		match self {
60			FlowDispatchError::InvalidWorkerId {
61				worker_id,
62				num_workers,
63			} => flow_invalid_worker_id(worker_id, num_workers),
64			FlowDispatchError::WorkerStopped {
65				worker_id,
66			} => flow_worker_stopped(worker_id),
67			FlowDispatchError::PoolBusy => flow_pool_busy(),
68			FlowDispatchError::CoordinatorBusy => flow_coordinator_busy(),
69			FlowDispatchError::CoordinatorStopped => flow_coordinator_stopped(),
70			FlowDispatchError::PoolActorStopped => flow_pool_actor_stopped(),
71			FlowDispatchError::KeyspaceOverlap {
72				key,
73			} => flow_transaction_keyspace_overlap(key),
74			FlowDispatchError::DictionaryWriteDivergence {
75				key,
76			} => flow_transaction_dictionary_write_divergence(key),
77			FlowDispatchError::WorkerFailed {
78				worker_id,
79				cause,
80			} => flow_worker_failed(worker_id, *cause.0),
81		}
82	}
83}
84
85impl From<FlowDispatchError> for Error {
86	fn from(err: FlowDispatchError) -> Self {
87		Error(Box::new(err.into_diagnostic()))
88	}
89}
90
91#[derive(Debug, thiserror::Error)]
92pub enum FlowStateError {
93	#[error("failed to serialize flow operator state '{state}': {cause}")]
94	Encode {
95		state: &'static str,
96		cause: String,
97	},
98
99	#[error("failed to deserialize flow operator state '{state}': {cause}")]
100	Decode {
101		state: &'static str,
102		cause: String,
103	},
104}
105
106impl IntoDiagnostic for FlowStateError {
107	fn into_diagnostic(self) -> Diagnostic {
108		match self {
109			FlowStateError::Encode {
110				state,
111				cause,
112			} => flow_state_encode_failed(state, cause),
113			FlowStateError::Decode {
114				state,
115				cause,
116			} => flow_state_decode_failed(state, cause),
117		}
118	}
119}
120
121impl From<FlowStateError> for Error {
122	fn from(err: FlowStateError) -> Self {
123		Error(Box::new(err.into_diagnostic()))
124	}
125}
126
127#[derive(Debug, thiserror::Error)]
128pub enum FlowGraphError {
129	#[error("flow node kind '{kind}' is not supported in persistent flows")]
130	UnsupportedNode {
131		kind: &'static str,
132	},
133
134	#[error("flow node '{node}' requires {expected} inputs, but the DAG provided {found}")]
135	NodeInputArity {
136		node: &'static str,
137		expected: &'static str,
138		found: usize,
139	},
140
141	#[error("parent operator not found while wiring flow node input: {input}")]
142	ParentOperatorNotFound {
143		input: String,
144	},
145
146	#[error("unknown flow operator '{operator}'")]
147	UnknownOperator {
148		operator: String,
149	},
150
151	#[error("FFI operators are not supported on the wasm target")]
152	FfiUnsupportedOnWasm,
153
154	#[error("flow node is missing a required input edge")]
155	MissingInputEdge,
156
157	#[error("{operator} operator received a diff from an unknown node")]
158	UnknownDiffOrigin {
159		operator: &'static str,
160		origin: Option<String>,
161	},
162
163	#[error("transactional flow {flow_id} references sink view {view_id} not visible at registration")]
164	SinkViewNotVisibleAtRegistration {
165		flow_id: u64,
166		view_id: u64,
167	},
168}
169
170impl IntoDiagnostic for FlowGraphError {
171	fn into_diagnostic(self) -> Diagnostic {
172		match self {
173			FlowGraphError::UnsupportedNode {
174				kind,
175			} => flow_unsupported_node(kind),
176			FlowGraphError::NodeInputArity {
177				node,
178				expected,
179				found,
180			} => flow_node_input_arity(node, expected, found),
181			FlowGraphError::ParentOperatorNotFound {
182				input,
183			} => flow_parent_operator_not_found(input),
184			FlowGraphError::UnknownOperator {
185				operator,
186			} => flow_unknown_operator(&operator),
187			FlowGraphError::FfiUnsupportedOnWasm => flow_ffi_unsupported_on_wasm(),
188			FlowGraphError::MissingInputEdge => flow_missing_input_edge(),
189			FlowGraphError::UnknownDiffOrigin {
190				operator,
191				origin,
192			} => flow_unknown_diff_origin(operator, origin),
193			FlowGraphError::SinkViewNotVisibleAtRegistration {
194				flow_id,
195				view_id,
196			} => flow_sink_view_not_visible_at_registration(flow_id, view_id),
197		}
198	}
199}
200
201impl From<FlowGraphError> for Error {
202	fn from(err: FlowGraphError) -> Self {
203		Error(Box::new(err.into_diagnostic()))
204	}
205}
206
207#[derive(Debug, thiserror::Error)]
208pub enum NativeOperatorError {
209	#[error("native operator ABI tag mismatch: plugin {plugin:#06x}, host {host:#06x}")]
210	AbiTagMismatch {
211		plugin: u32,
212		host: u32,
213	},
214
215	#[error("native operator library not loaded: {path}")]
216	LibraryNotLoaded {
217		path: String,
218	},
219
220	#[error("native operator symbol '{symbol}' not found: {cause}")]
221	SymbolNotFound {
222		symbol: &'static str,
223		cause: String,
224	},
225
226	#[error("native operator '{operator}' not found")]
227	OperatorNotFound {
228		operator: String,
229	},
230
231	#[error("failed to create native/FFI operator: {cause}")]
232	CreateFailed {
233		cause: String,
234	},
235}
236
237impl IntoDiagnostic for NativeOperatorError {
238	fn into_diagnostic(self) -> Diagnostic {
239		match self {
240			NativeOperatorError::AbiTagMismatch {
241				plugin,
242				host,
243			} => native_abi_tag_mismatch(plugin, host),
244			NativeOperatorError::LibraryNotLoaded {
245				path,
246			} => native_library_not_loaded(&path),
247			NativeOperatorError::SymbolNotFound {
248				symbol,
249				cause,
250			} => native_symbol_not_found(symbol, cause),
251			NativeOperatorError::OperatorNotFound {
252				operator,
253			} => native_operator_not_found(&operator),
254			NativeOperatorError::CreateFailed {
255				cause,
256			} => native_create_failed(cause),
257		}
258	}
259}
260
261impl From<NativeOperatorError> for Error {
262	fn from(err: NativeOperatorError) -> Self {
263		Error(Box::new(err.into_diagnostic()))
264	}
265}
266
267#[derive(Debug, thiserror::Error)]
268pub enum FlowSinkError {
269	#[error("row at index {row_idx} is missing the '{column}' system column")]
270	MissingSystemColumn {
271		column: &'static str,
272		row_idx: usize,
273	},
274
275	#[error("dictionary {dictionary_id} not found for view column '{column}'")]
276	DictionaryNotFound {
277		dictionary_id: String,
278		column: String,
279	},
280}
281
282impl IntoDiagnostic for FlowSinkError {
283	fn into_diagnostic(self) -> Diagnostic {
284		match self {
285			FlowSinkError::MissingSystemColumn {
286				column,
287				row_idx,
288			} => flow_sink_missing_system_column(column, row_idx),
289			FlowSinkError::DictionaryNotFound {
290				dictionary_id,
291				column,
292			} => flow_sink_dictionary_not_found(dictionary_id, &column),
293		}
294	}
295}
296
297impl From<FlowSinkError> for Error {
298	fn from(err: FlowSinkError) -> Self {
299		Error(Box::new(err.into_diagnostic()))
300	}
301}