wezterm/wezterm · error

invalid window id {}

Error message

invalid window id {}

What it means

Thrown by mux::termwizterm::run() after it registers a TermWizTerminalPane and tries to activate it in a caller-supplied window. register_tab() accepts an Option<WindowId>; when you pass Some(id), the code calls mux.get_window_mut(window_id) and that lookup fails because no window with that id exists in the Mux. It means the WindowId is stale or foreign: the window was closed, was purged by the mux, or the id came from a different Mux instance. Passing None instead lets the mux allocate a fresh window and never hits this path.

Source

Thrown at mux/src/termwiztermtab.rs:560

            None => {
                window_builder = mux.new_empty_window(None, None);
                *window_builder
            }
        };

        let pane =
            TermWizTerminalPane::new(domain.domain_id(), size, input_tx, render_rx, term_config);
        let pane: Arc<dyn Pane> = Arc::new(pane);

        let tab = Arc::new(Tab::new(&size));
        tab.assign_pane(&pane);

        mux.add_tab_and_active_pane(&tab)?;
        mux.add_tab_to_window(&tab, window_id)?;

        let mut window = mux
            .get_window_mut(window_id)
            .ok_or_else(|| anyhow::anyhow!("invalid window id {}", window_id))?;
        let tab_idx = window.len().saturating_sub(1);
        window.save_and_then_set_active(tab_idx);

        Ok((pane.pane_id(), window_id))
    }

    let (pane_id, window_id) = promise::spawn::spawn_into_main_thread(async move {
        register_tab(input_tx, render_rx, size, window_id, term_config).await
    })
    .await?;

    let result = promise::spawn::spawn_into_new_thread(move || f(tw_term)).await;

    // Since we're typically called with an outstanding Activity token active,
    // the dead status of the tab will be ignored until after the activity
    // resolves.  In the case of SSH where (currently!) several prompts may
    // be shown in succession, we don't want to leave lingering dead windows
    // on the screen so let's ask the mux to kill off our window now.

View on GitHub (pinned to 9c04f79f86)

Solutions

  1. Pass window_id: None so the mux creates (and owns) a fresh window instead of reusing a possibly-dead id
  2. Before calling run(), validate the id with mux.get_window(window_id).is_some() and fall back to None if it is gone
  3. If reusing a returned WindowId, re-validate it immediately before each subsequent call rather than caching it indefinitely
  4. Treat the error as non-fatal: catch it, retry run() with None

Example fix

// before
let (result, win_id) = run(size, Some(cached_window_id), f, cfg).await;

// after
let mux = Mux::get();
let window_id = if cached_window_id.map(|id| mux.get_window(id).is_some()).unwrap_or(false) {
    cached_window_id
} else {
    None // let the mux allocate a new window
};
let (result, win_id) = run(size, window_id, f, cfg).await;
Defensive patterns

Strategy: validation

Validate before calling

// Before mux::termwizterm::run(size, Some(window_id), f, cfg):
let mux = Mux::get();
let window_id = match window_id {
    Some(id) if mux.get_window(id).is_some() => Some(id),
    _ => None, // let the mux allocate a fresh window
};

Try / catch

match run(size, window_id, f, cfg).await {
    Ok(v) => v,
    Err(e) if e.to_string().starts_with("invalid window id") => {
        run(size, None, f, cfg).await? // retry in a new window
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling mux::termwizterm::run(size, Some(window_id), f, config) with a WindowId captured earlier (e.g. from a previous run() return value) after that window has been closed by the user or killed by mux.remove_window; reusing a WindowId across separate wezterm client sessions where the id was allocated in the other process's Mux.

Common situations: Hosting embedded termwiz prompts (SSH authentication/2FA dialogs) in an existing window that may have been dismissed while the async prompt chain was awaited; caching a WindowId across long awaits in Lua/CLI automation; races where the user closes the prompt window before a follow-up prompt is spawned.

Related errors


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