Hmbown/CodeWhale · error · ValidationReport
V9
V9
Error message
node {id} is terminal ({:?}) and cannot move to {:?}. Re-sending its current state is fine; changing it needs Supersede. What it means
patch_node is a validation guard in the work-graph reducer: it rejects a WorkNodePatch that moves a node out of a terminal state (e.g. cancelled/done), while re-asserting the current state stays a no-op. The message names the node id, its terminal state, and the attempted target, and tells the caller to use Supersede for genuine state changes — because work_update rewrites the whole todo list, re-sending cancelled items is expected and must not error.
Source
Thrown at crates/tui/src/work_graph/reducer.rs:258
/// a node out of a terminal state.
fn patch_node(
next: &mut WorkGraphSnapshot,
id: &WorkNodeId,
patch: &WorkNodePatch,
now: i64,
) -> Result<(), ValidationReport> {
let node = next
.node_mut(id)
.ok_or_else(|| structural(format!("node {id} not found")))?;
// Only an actual transition OUT of a terminal state is forbidden.
// Re-asserting the state a node already holds is a no-op, and rejecting it
// broke the only tool that writes here: `work_update` replaces the whole
// todo list on every call, so once an item is cancelled every later call
// re-sends it as cancelled and the entire update was refused. The model was
// then told to "use Supersede", which `work_update` does not expose — a
// dead end whose only escape was silently dropping the item from the list.
if node.state.is_terminal() && patch.state.is_some_and(|s| s != node.state) {
return Err(ValidationReport::single(
ValidationCode::V9,
format!(
"node {id} is terminal ({:?}) and cannot move to {:?}. \
Re-sending its current state is fine; changing it needs Supersede.",
node.state,
patch.state.expect("checked above")
),
));
}
if let Some(title) = &patch.title {
node.title = title.clone();
}
if let Some(state) = patch.state {
node.state = state;
}
if let Some(acceptance) = &patch.acceptance {
node.acceptance = acceptance.clone();
}View on GitHub (pinned to 0c42157ee5)
Solutions
- Treat re-sending the node's current terminal state as a no-op rather than an error
- To change a finished/cancelled node, send a Supersede patch instead of a direct state transition
- If this fires unexpectedly, inspect the patch stream for a stale writer replaying old state as a change
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at crates/tui/src/work_graph/reducer.rs:258 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/ba844b356daf6ffb.
Report an issue: GitHub.