sinelaw/fresh · critical

active window must have a populated split layout

Error message

active window must have a populated split layout

What it means

`focus_split` asserts that the active window has a split layout via `splits().expect(...)`. If the active window's split tree is absent or empty, the internal invariant is broken and the process panics. The editor assumes a newly focused window always has a populated split root.

Solutions

  1. Ensure the window's split layout is initialized (a root split exists) before focus handling runs
  2. Guard focus handling: skip when splits() returns None and log instead of panicking
  3. Reproduce with the failing state and fix where the split tree was dropped/not created
  4. Convert the expect to a graceful early-return in input-handling code paths

Example fix

// before
let in_main_tree = self.buffers.splits().expect("active window must have a populated split layout").0.root().leaf_split_ids().contains(&split_id);
// after
let Some(splits) = self.buffers.splits() else { return; };
let in_main_tree = splits.0.root().leaf_split_ids().contains(&split_id);
Defensive patterns

Strategy: type-guard

Validate before calling

if self.buffers.splits().is_none() || self.buffers.splits().unwrap().0.root().leaf_split_ids().is_empty() {
    return; // window not ready for split focus
}

Type guard

fn has_split_layout(buffers: &Buffers) -> bool {
    buffers.splits().map(|(tree, _)| !tree.root().leaf_split_ids().is_empty()).unwrap_or(false)
}

Prevention

When it happens

Trigger: Calling `focus_split` when the active window has no populated split layout — e.g. no buffers open yet, or window state created without initializing the split tree, then routing a click/keystroke that focuses `split_id`.

Common situations: Focusing a split before the first buffer opens; a regression in window teardown leaving splits() None/empty; automated tests constructing a window without populating splits.

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 sinelaw/fresh@67894ca546 (2026-09-13). Data as JSON: /api/errors/5872acfe9b9e0fc9. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-editor/src/app/active_focus.rs:269

        let previous_buffer = self.active_buffer(); // Get BEFORE changing split
        let split_changed = previous_split != split_id;

        // Preview is anchored to the split it was opened in. Moving focus to
        // a different split commits the preview — walking away is commitment.
        if split_changed {
            self.promote_preview_if_not_in_split(split_id);
        }

        // If `split_id` is not in the main split tree, it must be an inner
        // leaf of a Grouped subtree stashed in `grouped_subtrees`. For those
        // we don't change `split_manager.active_split` (the group's host
        // split remains active). Instead, find the host split and update
        // its `focused_group_leaf` marker so `active_buffer()` routes to
        // the clicked inner panel buffer.
        let in_main_tree = self
            .buffers
            .splits()
            .expect("active window must have a populated split layout")
            .0
            .root()
            .leaf_split_ids()
            .contains(&split_id);
        if !in_main_tree {
            // Find which group contains this inner leaf.
            let group_leaf_id = self
                .grouped_subtrees
                .iter()
                .find(|(_, node)| {
                    if let crate::view::split::SplitNode::Grouped { layout, .. } = node {
                        layout.find(split_id.into()).is_some()
                    } else {
                        false
                    }
                })
                .map(|(group_leaf_id, _)| *group_leaf_id);
            let host_split = group_leaf_id.and_then(|group_leaf_id| {

View on GitHub (pinned to 67894ca546)