Skip to main content

async_nodes/
error.rs

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