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

MovePaneBackwardsByPaneId missing pane_id

Error message

MovePaneBackwardsByPaneId missing pane_id

What it means

MovePaneBackwardsByPaneId swaps one specific pane backwards in the pane order. Its protobuf message MovePaneBackwardsByPaneIdAction has a single optional pane_id field; the internal Action requires a concrete PaneId. A message with pane_id = None fails conversion with 'MovePaneBackwardsByPaneId missing pane_id'.

Source

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

                    resize,
                    direction,
                })
            },
            ActionType::MovePaneByPaneId(a) => {
                let direction = a.direction.map(|d| proto_i32_to_direction(d)).transpose()?;
                Ok(crate::input::actions::Action::MovePaneByPaneId {
                    pane_id: a
                        .pane_id
                        .ok_or_else(|| anyhow!("MovePaneByPaneId missing pane_id"))?
                        .try_into()?,
                    direction,
                })
            },
            ActionType::MovePaneBackwardsByPaneId(a) => {
                Ok(crate::input::actions::Action::MovePaneBackwardsByPaneId {
                    pane_id: a
                        .pane_id
                        .ok_or_else(|| anyhow!("MovePaneBackwardsByPaneId missing pane_id"))?
                        .try_into()?,
                })
            },
            ActionType::ClearScreenByPaneId(a) => {
                Ok(crate::input::actions::Action::ClearScreenByPaneId {
                    pane_id: a
                        .pane_id
                        .ok_or_else(|| anyhow!("ClearScreenByPaneId missing pane_id"))?
                        .try_into()?,
                })
            },
            ActionType::EditScrollbackByPaneId(a) => {
                Ok(crate::input::actions::Action::EditScrollbackByPaneId {
                    pane_id: a
                        .pane_id
                        .ok_or_else(|| anyhow!("EditScrollbackByPaneId missing pane_id"))?
                        .try_into()?,
                    ansi: a.ansi,

View on GitHub (pinned to 98a0837077)

Solutions

  1. Populate pane_id: Some(PaneId { pane_type: Some(PaneType::Terminal(id)) }).
  2. Use MovePaneBackwards for the focused pane.
  3. Rebuild the sender against the current zellij-utils contract.
  4. Guard with a helper that rejects ByPaneId messages lacking a pane id before sending.

Example fix

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

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

Strategy: validation

Validate before calling

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

Type guard

fn has_valid_pane_id(a: &MovePaneBackwardsByPaneIdAction) -> 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("missing pane_id") => {
        log::warn!("dropping malformed move action: {e}");
    },
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Sending ActionType::MovePaneBackwardsByPaneId while MovePaneBackwardsByPaneIdAction.pane_id is None.

Common situations: Scripts or plugins reordering panes via IPC; prost structs created with default(); stale generated contract on the sender; meaning to use the focused-pane MovePaneBackwards variant.

Related errors


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