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, &region_a, count_a, splits);
                b.compute_splits(spacing, min_size, &region_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

  1. Recompute the count tree from the exact same node tree right before computing splits.
  2. Persist and restore node + count trees as a single unit; validate the loaded structure by re-walking nodes.
  3. Ensure each split/close mutation regenerates counts before the next layout pass.
  4. 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

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


AI-assisted analysis of iced-rs/iced@d146509d89 (2026-09-11). Data as JSON: /api/errors/0376ec0016c83e29. Report an issue: GitHub.