{"record":{"id":"5872acfe9b9e0fc9","repo":"sinelaw/fresh","slug":"active-window-must-have-a-populated-split-layout","errorCode":null,"errorMessage":"active window must have a populated split layout","messagePattern":"active window must have a populated split layout","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/fresh-editor/src/app/active_focus.rs","lineNumber":269,"sourceCode":"        let previous_buffer = self.active_buffer(); // Get BEFORE changing split\n        let split_changed = previous_split != split_id;\n\n        // Preview is anchored to the split it was opened in. Moving focus to\n        // a different split commits the preview — walking away is commitment.\n        if split_changed {\n            self.promote_preview_if_not_in_split(split_id);\n        }\n\n        // If `split_id` is not in the main split tree, it must be an inner\n        // leaf of a Grouped subtree stashed in `grouped_subtrees`. For those\n        // we don't change `split_manager.active_split` (the group's host\n        // split remains active). Instead, find the host split and update\n        // its `focused_group_leaf` marker so `active_buffer()` routes to\n        // the clicked inner panel buffer.\n        let in_main_tree = self\n            .buffers\n            .splits()\n            .expect(\"active window must have a populated split layout\")\n            .0\n            .root()\n            .leaf_split_ids()\n            .contains(&split_id);\n        if !in_main_tree {\n            // Find which group contains this inner leaf.\n            let group_leaf_id = self\n                .grouped_subtrees\n                .iter()\n                .find(|(_, node)| {\n                    if let crate::view::split::SplitNode::Grouped { layout, .. } = node {\n                        layout.find(split_id.into()).is_some()\n                    } else {\n                        false\n                    }\n                })\n                .map(|(group_leaf_id, _)| *group_leaf_id);\n            let host_split = group_leaf_id.and_then(|group_leaf_id| {","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/sinelaw/fresh/blob/67894ca5463dbd7a89bb31add4627c27d6b79d83/crates/fresh-editor/src/app/active_focus.rs#L251-L287","documentation":"`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.","triggerScenarios":"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`.","commonSituations":"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.","solutions":["Ensure the window's split layout is initialized (a root split exists) before focus handling runs","Guard focus handling: skip when splits() returns None and log instead of panicking","Reproduce with the failing state and fix where the split tree was dropped/not created","Convert the expect to a graceful early-return in input-handling code paths"],"exampleFix":"// before\nlet in_main_tree = self.buffers.splits().expect(\"active window must have a populated split layout\").0.root().leaf_split_ids().contains(&split_id);\n// after\nlet Some(splits) = self.buffers.splits() else { return; };\nlet in_main_tree = splits.0.root().leaf_split_ids().contains(&split_id);","handlingStrategy":"type-guard","validationCode":"if self.buffers.splits().is_none() || self.buffers.splits().unwrap().0.root().leaf_split_ids().is_empty() {\n    return; // window not ready for split focus\n}","typeGuard":"fn has_split_layout(buffers: &Buffers) -> bool {\n    buffers.splits().map(|(tree, _)| !tree.root().leaf_split_ids().is_empty()).unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Initialize the split tree at window creation, before input handling","Never allow the active window to have an unpopulated layout; treat it as a lifecycle bug","Add debug assertions/tests that splits() is Some in focus paths","Convert expects in input handlers to logged early-returns"],"tags":["panic","invariant","splits","editor"],"backgroundTag":"internal-invariant-violation","analyzedSha":"67894ca5463dbd7a89bb31add4627c27d6b79d83","analyzedAt":"2026-09-13T15:04:03.701Z","contentChangedAt":"2026-09-13T15:04:03.701Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}