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

CloseFocusByPaneId missing pane_id

Error message

CloseFocusByPaneId missing pane_id

What it means

CloseFocusByPaneId closes one specific pane (the ByPaneId form of CloseFocus). Its protobuf message CloseFocusByPaneIdAction has one optional pane_id field which the internal Action requires. With pane_id = None the conversion fails with 'CloseFocusByPaneId missing pane_id', so the pane is never closed.

Source

Thrown at zellij-utils/src/ipc/protobuf_conversion.rs:3057

                        .ok_or_else(|| anyhow!("ToggleNoUiFullscreenByPaneId missing pane_id"))?
                        .try_into()?,
                },
            ),
            ActionType::TogglePaneEmbedOrFloatingByPaneId(a) => Ok(
                crate::input::actions::Action::TogglePaneEmbedOrFloatingByPaneId {
                    pane_id: a
                        .pane_id
                        .ok_or_else(|| {
                            anyhow!("TogglePaneEmbedOrFloatingByPaneId missing pane_id")
                        })?
                        .try_into()?,
                },
            ),
            ActionType::CloseFocusByPaneId(a) => {
                Ok(crate::input::actions::Action::CloseFocusByPaneId {
                    pane_id: a
                        .pane_id
                        .ok_or_else(|| anyhow!("CloseFocusByPaneId missing pane_id"))?
                        .try_into()?,
                })
            },
            ActionType::RenamePaneByPaneId(a) => {
                Ok(crate::input::actions::Action::RenamePaneByPaneId {
                    pane_id: a.pane_id.map(|p| p.try_into()).transpose()?,
                    name: a.name,
                })
            },
            ActionType::UndoRenamePaneByPaneId(a) => {
                Ok(crate::input::actions::Action::UndoRenamePaneByPaneId {
                    pane_id: a
                        .pane_id
                        .ok_or_else(|| anyhow!("UndoRenamePaneByPaneId missing pane_id"))?
                        .try_into()?,
                })
            },
            ActionType::TogglePanePinnedByPaneId(a) => {

View on GitHub (pinned to 98a0837077)

Solutions

  1. Set pane_id: Some(PaneId { pane_type: Some(PaneType::Terminal(id)) }).
  2. Use the plain CloseFocus action for the focused pane.
  3. Never forward an unresolved Option pane id into the action; fall back or error at the call site.
  4. Rebuild custom clients against the current contract.

Example fix

// before
let action = ActionType::CloseFocusByPaneId(
    CloseFocusByPaneIdAction { pane_id: None },
); // -> "CloseFocusByPaneId missing pane_id"

// after
let action = ActionType::CloseFocusByPaneId(
    CloseFocusByPaneIdAction {
        pane_id: Some(PaneId { pane_type: Some(PaneType::Terminal(1)) }),
    },
);
Defensive patterns

Strategy: validation

Validate before calling

fn is_sendable(a: &CloseFocusByPaneIdAction) -> bool {
    a.pane_id
        .as_ref()
        .and_then(|p| p.pane_type.as_ref())
        .is_some()
}

Type guard

fn has_valid_pane_id(a: &CloseFocusByPaneIdAction) -> bool {
    matches!(
        a.pane_id.as_ref().and_then(|p| p.pane_type.as_ref()),
        Some(pane_id::PaneType::Terminal(_)) | Some(pane_id::PaneType::Plugin(_))
    )
}

Try / catch

match Action::try_from(proto_action) {
    Ok(action) => dispatch(action),
    Err(e) if e.to_string().contains("CloseFocusByPaneId missing pane_id") => {
        log::warn!("close action without pane target - pane NOT closed: {e}");
    },
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Sending ActionType::CloseFocusByPaneId while CloseFocusByPaneIdAction.pane_id is None.

Common situations: Plugins closing panes by id where the id lookup failed and None was forwarded; default-constructed messages; contract version skew; intending the plain CloseFocus variant.

Related errors


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