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

  1. 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.
  2. Fix the window-close path to keep `active_window` valid at all times.
  3. Route tab-close UI events through a check that the owning window still exists before calling `close_buffer`.
  4. 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

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


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 buffer

View on GitHub (pinned to 67894ca546)