Node Errors
Node functions can be either infallible (returning a bare tuple) or fallible (returning Result).
Infallible nodes
The simplest nodes return a bare tuple. They cannot fail:
Node {
name: "double",
func: |x: i32| (x * 2,),
input: (¶ms.x,),
output: (&cat.doubled,),
}
Fallible nodes
Nodes that can fail return Result<(outputs...), E> where E is the pipeline error type:
Node {
name: "parse",
func: |text: String| -> Result<(i32,), PondError> {
let n = text.trim().parse::<i32>()
.map_err(|e| PondError::Custom(e.to_string()))?;
Ok((n,))
},
input: (&cat.raw_text,),
output: (&cat.parsed,),
}
How it works: IntoNodeResult
The IntoNodeResult trait normalizes both bare tuples and Result returns into Result<O, E>:
pub trait IntoNodeResult<O, E> {
fn into_node_result(self) -> Result<O, E>;
}
- For bare tuples
O: wraps inOk(value) - For
Result<O, E>: passes through unchanged
This means the same Node struct works for both infallible and fallible functions.
CompatibleOutput
The CompatibleOutput trait ensures at compile time that the function’s return type matches the output datasets. It accepts both:
O(bare tuple) — direct matchResult<O, E>— theOktype must match
If the types don’t match, you get a compile error when constructing the Node, not at runtime.
Error propagation
When a fallible node returns Err(e):
- The runner catches the error
- The
on_node_errorhook fires with the error message - If the node is inside a
Pipeline,on_pipeline_errorfires for each ancestor - The runner stops executing and returns the error
Using custom error types
With a custom error type, nodes can return domain-specific errors:
#[derive(Debug, thiserror::Error)]
enum MyError {
#[error(transparent)]
Pond(#[from] PondError),
#[error("value {0} exceeds limit")]
TooLarge(f64),
}
Node {
name: "validate",
func: |value: f64| -> Result<(f64,), MyError> {
if value > 100.0 {
return Err(MyError::TooLarge(value));
}
Ok((value,))
},
input: (&cat.raw,),
output: (&cat.validated,),
}