1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use thiserror::Error;
use super::graph::{EdgeId, NodeId, PortIndex};
/// Standardized error types for the BSP graph compiler and execution runtime.
#[derive(Debug, Clone, Error)]
pub enum BspError {
#[error("Node {node_id} ('{node_name}') failed execution: {message}")]
NodeExecutionFailed {
node_id: NodeId,
node_name: String,
message: String,
},
#[error(
"Type mismatch on node {node_id}, port {port_index}: Expected {expected}, found {found}"
)]
TypeMismatch {
node_id: NodeId,
port_index: PortIndex,
expected: &'static str,
found: &'static str,
},
#[error("Cycle detected in graph topology at or near node {node_id}")]
CycleDetected { node_id: NodeId },
#[error("Unconnected required input port {port_index} on node {node_id}")]
UnconnectedPort {
node_id: NodeId,
port_index: PortIndex,
},
#[error("Node ID {node_id} was requested but does not exist in the graph registry")]
NodeNotFound { node_id: NodeId },
#[error(
"Graph execution reached an unsolvable deadlocked state due to unresolved data dependencies"
)]
Deadlock,
#[error("Graph compilation or validation failed: {0}")]
ValidationError(String),
/// A cycle-closing edge fired more than its configured `max_iterations` budget.
///
/// This is the per-edge circuit breaker for control-flow loops: every edge that closes a
/// cycle should carry a `max_iterations` cap, and exceeding it aborts the run loudly
/// instead of looping forever.
#[error(
"Loop bound exceeded on edge {edge} ({from} -> {to}): fired more than the configured {max} iteration budget"
)]
LoopBoundExceeded {
edge: EdgeId,
from: NodeId,
to: NodeId,
max: u64,
},
/// A single edge accumulated more pending (unconsumed) activation tokens than allowed.
///
/// Guards against unbounded memory growth when a misconfigured loop re-queues tokens
/// faster than the target node consumes them.
#[error(
"Pending token limit exceeded on edge {edge} ({from} -> {to}): more than {cap} unconsumed tokens"
)]
TokenLimitExceeded {
edge: EdgeId,
from: NodeId,
to: NodeId,
cap: u64,
},
/// A node was executed more times than its configured `max_executions` budget.
///
/// Useful as a circuit breaker for poll-until style nodes that must not run forever.
#[error("Node {node_id} exceeded its configured maximum of {max} executions")]
MaxExecutionsExceeded { node_id: NodeId, max: u64 },
/// A graph definition (`GraphDef`) could not be parsed or instantiated.
///
/// Unknown node `type_name`s, condition names, and merger names are reported through
/// [`BspError::ValidationError`] instead (registry completeness is a validation concern);
/// this variant covers structural definition faults such as an unsupported `version`.
#[error("Graph definition error: {0}")]
DefError(String),
/// Serializing or deserializing a [`super::run::SuspendedExecution`] snapshot failed.
#[error("Suspension snapshot (de)serialization failed: {0}")]
SuspensionSerde(String),
}