iced-rs/iced · error
Node configuration and split count do not match
Error message
Node configuration and split count do not match
What it means
`Node::compute_splits` pairs the pane_grid `Node` tree with a `Count` tree while walking, and hits an incompatible node/count combination (e.g. `Node::Pane` with `Count::Split`). The two structures were built from different tree versions, so the library declares the case unreachable and panics.
Source
Thrown at widget/src/pane_grid/node.rs:340
Axis::Vertical => (count_a.vertical(), count_b.vertical()),
};
let (region_a, region_b, ratio) = axis.split(
current,
*ratio,
spacing,
min_size * (a_factor + 1) as f32 + spacing * a_factor as f32,
min_size * (b_factor + 1) as f32 + spacing * b_factor as f32,
);
let _ = splits.insert(*id, (*axis, *current, ratio));
a.compute_splits(spacing, min_size, ®ion_a, count_a, splits);
b.compute_splits(spacing, min_size, ®ion_b, count_b, splits);
}
(Node::Pane(_), Count::Pane) => {}
_ => {
unreachable!("Node configuration and split count do not match")
}
}
}
}
impl std::hash::Hash for Node {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Node::Split {
id,
axis,
ratio,
a,
b,
} => {
id.hash(state);
axis.hash(state);
((ratio * 100_000.0) as u32).hash(state);View on GitHub (pinned to d146509d89)
Solutions
- Recompute the count tree from the exact same node tree right before computing splits.
- Persist and restore node + count trees as a single unit; validate the loaded structure by re-walking nodes.
- Ensure each split/close mutation regenerates counts before the next layout pass.
- If it occurs in stock usage, report as an internal desync bug with a reproduction.
Example fix
// before splits_from_cache = cached_splits(state_at_load_time); node.compute_splits(..., splits_from_cache, ...); // after splits = state.split_counts(); // recomputed from the same Node tree node.compute_splits(..., splits, ...);
Defensive patterns
Strategy: validation
Validate before calling
fn splits_match(node: &Node, count: &Count) -> bool {
match (node, count) {
(Node::Split { a, b }, Count::Split { a: ca, b: cb }) => {
splits_match(a, ca) && splits_match(b, cb)
}
(Node::Pane(_), Count::Pane) => true,
_ => false,
}
} Type guard
fn is_valid_pair(node: &Node, count: &Count) -> bool {
!matches!(
(node, count),
(Node::Split { .. }, Count::Pane) | (Node::Pane(_), Count::Split { .. })
)
} Try / catch
// Guard before calling split_regions assert!(splits_match(&node, &count), "regenerate split counts from the node tree"); node.compute_splits(...);
Prevention
- Recompute split counts immediately before each layout pass.
- Persist node and count trees together; regenerate counts when deserialization is suspected stale.
- Apply split/close mutations and count updates in the same message-processing step.
When it happens
Trigger: Calling `split_regions`/layout code where the split-count tree does not mirror the current node tree: a split was added or removed after counts were computed, or a reconstructed node tree disagrees with persisted counts.
Common situations: Restoring a saved pane layout where the node tree was edited (splits added/removed) without regenerating counts; concurrent mutation of pane state; mismatched snapshots in custom layout tooling.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Node configuration and count do not match
- Caches must not be empty
- Downcast widget state
- Downcast on stateless state
- {error:?}
AI-assisted analysis of iced-rs/iced@d146509d89 (2026-09-11).
Data as JSON: /api/errors/0376ec0016c83e29.
Report an issue: GitHub.