wezterm/wezterm · error

Invalid tab id {}

Error message

Invalid tab id {}

What it means

TmuxDomainState::split_pane() (tmux_commands.rs:233) resolves a completed remote split into a local pane. First it looks up the local Tab by TabId in the Mux; when mux.get_tab(tab_id) returns None this bail fires. The Tab was removed — the tmux window was closed, the local window containing it was killed, or the mux is shutting down — while a split request for it was still in flight.

Source

Thrown at mux/src/tmux_commands.rs:243

            Box::new(child),
            Box::new(pane_pty),
            Box::new(writer),
            self.domain_id,
            "tmux pane".to_string(),
        )))
    }

    pub fn split_pane(
        &self,
        tab_id: TabId,
        pane_id: PaneId,
        remote_id: TmuxPaneId,
        split_request: SplitRequest,
    ) -> anyhow::Result<Arc<dyn Pane>> {
        let mux = Mux::get();
        let tab = match mux.get_tab(tab_id) {
            Some(t) => t,
            None => anyhow::bail!("Invalid tab id {}", tab_id),
        };

        let pane_index = match tab
            .iter_panes_ignoring_zoom()
            .iter()
            .find(|p| p.pane.pane_id() == pane_id)
        {
            Some(p) => p.index,
            None => anyhow::bail!("invalid pane id {}", pane_id),
        };

        let split_size = match tab.compute_split_size(pane_index, split_request) {
            Some(s) => s,
            None => anyhow::bail!("invalid pane index {}", pane_index),
        };

        let window_id = match self.gui_tabs.lock().iter().find(|t| t.1.tab_id == tab_id) {
            Some((_, tab)) => tab.tmux_window_id,

View on GitHub (pinned to 9c04f79f86)

Solutions

  1. Check mux.get_tab(tab_id).is_some() before resolving the pending split; drop the split if the tab is gone
  2. Abort pending_splits promises when a tab is removed so stale resolutions never run
  3. On this error, discard the remote pane id (the tmux pane will be pruned by the next ListAllPanes sync)

Example fix

// before
let tab = match mux.get_tab(tab_id) {
    Some(t) => t,
    None => anyhow::bail!("Invalid tab id {}", tab_id),
};

// after
let Some(tab) = mux.get_tab(tab_id) else {
    log::debug!("tab {tab_id} gone; dropping split for pane {pane_id}");
    return Ok(None); // caller skips pane creation
};
Defensive patterns

Strategy: validation

Validate before calling

// Before resolving a pending split:
if mux.get_tab(tab_id).is_none() {
    // tab closed while split was in flight; drop the split
    return Ok(None);
}

Type guard

fn tab_alive(mux: &Mux, tab_id: TabId) -> bool {
    mux.get_tab(tab_id).is_some()
}

Try / catch

match state.split_pane(tab_id, pane_id, remote_id, req) {
    Ok(pane) => Some(pane),
    Err(e) if e.to_string().starts_with("Invalid tab id") => {
        None // stale split: let next ListAllPanes prune the remote pane
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: A pending split promise resolving (WindowPaneChanged) after the user closed the window/tab in wezterm; mux.remove_tab running between the split request (Domain::split_pane captured tab_id) and the split_pane resolution call; GUI shutdown dropping tabs mid-operation.

Common situations: Closing a tmux-attached wezterm window immediately after issuing a split; rapid create/split/close sequences during scripting or UI automation.

Related errors


AI-assisted analysis of wezterm/wezterm@9c04f79f86 (2026-08-16). Data as JSON: /api/errors/0923172d5d27cb31. Report an issue: GitHub.