sinelaw/fresh · error
active window present
Error message
active window present
What it means
Panic from `.expect("active window present")` at the top of the public `close_buffer` (buffer_close.rs:40). Before closing, it checks the target buffer for unsaved changes by looking it up through the active window; the expect asserts the active window exists. It panics when `close_buffer` is called (e.g. from `close_tab_in_split`, `preview_file`, `dismiss_preview`, terminal close) while `active_window` no longer resolves in `self.windows`.
Solutions
- Guard the lookup: if the active window is missing, treat the buffer as absent and proceed with the close (or return early) instead of panicking.
- Fix the window-close path to keep `active_window` valid at all times.
- Route tab-close UI events through a check that the owning window still exists before calling `close_buffer`.
- Write a test closing a buffer while the active window is stale to lock in non-panicking behavior.
Example fix
// before
.map(|w| &w.buffers)
.expect("active window present")
.get(&id)
// after
let has_unsaved = self
.windows
.get(&self.active_window)
.and_then(|w| w.buffers.get(&id))
.map(|state| state.buffer.is_modified())
.unwrap_or(false);
if has_unsaved {
return Err(anyhow::anyhow!("Buffer has unsaved changes"));
} Defensive patterns
Strategy: validation
Validate before calling
// before calling close_buffer:
if !app.windows.contains_key(&app.active_window) {
// window already gone; buffer close is a no-op or should be routed elsewhere
return;
} Type guard
fn buffer_in_active_window(app: &App, id: BufferId) -> bool {
app.windows.get(&app.active_window)
.map(|w| w.buffers.contains_key(&id))
.unwrap_or(false)
} Try / catch
match std::panic::catch_unwind(AssertUnwindSafe(|| app.close_buffer(id))) {
Ok(res) => res?,
Err(_) => { /* treat as buffer already gone */ }
} Prevention
- Make close_buffer tolerate a missing active window (unwrap_or semantics) since its result only depends on the target buffer.
- Verify the owning window exists before dispatching tab-close UI events.
- Keep active_window valid across every window removal.
- Add a regression test for the issue #1620 family of click-then-panic paths.
When it happens
Trigger: Any caller (`close_tab_in_split`, `close_tab_in_split_silent`, `preview_file`, `dismiss_preview`, `handle_close_buffer`, `handle_close_buffer`-family `handle_close_terminal`) invokes `close_buffer(id)` after the active window was removed from `self.windows` without updating `self.active_window`.
Common situations: Clicking a tab close button (issue-#1620-adjacent UI paths) during window teardown; scripted keybindings closing buffers after the window is gone; preview dismissal racing window close.
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
- active window present
- active window must have a populated split layout
- active window present
- active window present
- has_other_tab
AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13).
Data as JSON: /api/errors/39ef1367c5d6c936.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fresh-editor/src/app/buffer_close.rs:40
/// Buffer the host split's `active_buffer` becomes.
buffer: BufferId,
/// `true` when no other buffer existed and a fresh empty one was created.
created_empty: bool,
/// Set when the LRU landing target was a buffer *group* rather than a
/// buffer: `buffer` is then only housekeeping and the caller re-activates
/// the group tab on this leaf.
return_to_group: Option<LeafId>,
}
impl Editor {
/// Close the given buffer
pub fn close_buffer(&mut self, id: BufferId) -> anyhow::Result<()> {
// Check for unsaved changes
if let Some(state) = self
.windows
.get(&self.active_window)
.map(|w| &w.buffers)
.expect("active window present")
.get(&id)
{
if state.buffer.is_modified() {
return Err(anyhow::anyhow!("Buffer has unsaved changes"));
}
}
self.close_buffer_internal(id, false)
}
/// Force close the given buffer without checking for unsaved changes
/// Use this when the user has already confirmed they want to discard changes
pub fn force_close_buffer(&mut self, id: BufferId) -> anyhow::Result<()> {
self.close_buffer_internal(id, false)
}
/// Close the last editor tab while a Utility Dock is present, keeping the
/// editor leaf alive but empty (issue #2283). The editor leaf we intend to
/// keep is made the active split so the synthesized placeholder bufferView on GitHub (pinned to 67894ca546)