zellij-org/zellij · error · anyhow::Error

active tab {} does not exist

Error message

active tab {} does not exist

What it means

Screen::get_active_tab maps a client's entry in active_tab_ids to a Tab in the tabs BTreeMap. This error means the client's recorded active tab id has no corresponding Tab — the two structures are out of sync, e.g. the tab was closed without updating active_tab_ids for that client.

Source

Thrown at zellij-server/src/screen.rs:4280

        Ok(())
    }

    /// Returns a mutable reference to this [`Screen`]'s tabs.
    pub fn get_tabs_mut(&mut self) -> &mut BTreeMap<usize, Tab> {
        &mut self.tabs
    }

    pub fn get_tabs(&self) -> &BTreeMap<usize, Tab> {
        &self.tabs
    }

    /// Returns an immutable reference to this [`Screen`]'s active [`Tab`].
    pub fn get_active_tab(&self, client_id: ClientId) -> Result<&Tab> {
        match self.active_tab_ids.get(&client_id) {
            Some(tab) => self
                .tabs
                .get(tab)
                .ok_or_else(|| anyhow!("active tab {} does not exist", tab)),
            None => Err(anyhow!("active tab not found for client {:?}", client_id)),
        }
    }

    pub fn get_client_input_mode(&self, client_id: ClientId) -> Option<InputMode> {
        self.get_active_tab(client_id)
            .ok()
            .and_then(|tab| tab.get_client_input_mode(client_id))
    }

    pub fn get_first_client_id(&self) -> Option<ClientId> {
        self.active_tab_ids.keys().next().copied()
    }

    /// Returns an immutable reference to this [`Screen`]'s previous active [`Tab`].
    /// Consumes the last entry in tab history.
    pub fn get_previous_tab(&mut self, client_id: ClientId) -> Result<Option<&Tab>> {
        Ok(

View on GitHub (pinned to 98a0837077)

Solutions

  1. Update or clear the client's active tab pointer when closing tabs (set_active_tab / the tab-close handlers) so active_tab_ids never outlives the tab
  2. Callers should handle the Err and pick a fallback tab (e.g. first key of self.tabs) instead of crashing
  3. Reproduce and report upstream if a stock keybinding flow leaves the desync behind

Example fix

// before
let tab = screen.get_active_tab(client_id)?;

// after
let tab = screen
    .get_active_tab(client_id)
    .or_else(|_| screen.get_tabs().values().next().context("no tabs exist"))?;
Defensive patterns

Strategy: fallback

Type guard

fn client_tab_alive(active_tab_ids: &HashMap<ClientId, usize>, tabs: &BTreeMap<usize, Tab>, client: ClientId) -> bool {
    active_tab_ids.get(&client).map_or(false, |id| tabs.contains_key(id))
}

Try / catch

let tab = screen
    .get_active_tab(client_id)
    .or_else(|_| screen.get_tabs().values().next().context("no tabs exist"))?;

Prevention

When it happens

Trigger: Any caller of get_active_tab (rendering, input-mode queries, focus operations) after the tab referenced by active_tab_ids[client_id] was removed from self.tabs — close-tab paths that skip updating the client's active pointer, or a client reconnecting with stale state.

Common situations: Closing the active tab of a client in multi-client sessions; plugin actions closing arbitrary tabs; resurrection/session-restore rehydrating clients with stale active tab ids.

Related errors


AI-assisted analysis of zellij-org/zellij@98a0837077 (2026-08-16). Data as JSON: /api/errors/874ee2aa68d03c50. Report an issue: GitHub.