zellij-org/zellij · error · anyhow::Error
MovePaneByPaneId missing pane_id
Error message
MovePaneByPaneId missing pane_id
What it means
MovePaneByPaneId moves one specific pane inside the layout, optionally in a Direction. Its protobuf message MovePaneByPaneIdAction carries optional pane_id and optional direction; direction may legitimately be None (conversion maps it to None), but pane_id is mandatory. If pane_id is None the conversion fails with 'MovePaneByPaneId missing pane_id'.
Source
Thrown at zellij-utils/src/ipc/protobuf_conversion.rs:2997
let direction = resize_action
.direction
.map(|d| proto_i32_to_direction(d))
.transpose()?;
Ok(crate::input::actions::Action::ResizeByPaneId {
pane_id: a
.pane_id
.ok_or_else(|| anyhow!("ResizeByPaneId missing pane_id"))?
.try_into()?,
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()?,View on GitHub (pinned to 98a0837077)
Solutions
- Set pane_id: Some(PaneId { pane_type: Some(PaneType::Terminal(id)) }); direction stays optional.
- Use the plain MovePane variant when moving the focused pane.
- Align all IPC participants on the same zellij release.
- Add a unit test that converts every action variant you emit to catch missing fields early.
Example fix
// before
let action = ActionType::MovePaneByPaneId(MovePaneByPaneIdAction {
pane_id: None, // -> "MovePaneByPaneId missing pane_id"
direction: Some(Direction::Right as i32),
});
// after
let action = ActionType::MovePaneByPaneId(MovePaneByPaneIdAction {
pane_id: Some(PaneId { pane_type: Some(PaneType::Terminal(1)) }),
direction: Some(Direction::Right as i32),
}); Defensive patterns
Strategy: validation
Validate before calling
fn is_sendable(a: &MovePaneByPaneIdAction) -> bool {
a.pane_id
.as_ref()
.and_then(|p| p.pane_type.as_ref())
.is_some() // direction is genuinely optional here
} Type guard
fn has_valid_pane_id(a: &MovePaneByPaneIdAction) -> 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("MovePaneByPaneId missing pane_id") => {
log::warn!("move action without pane target: {e}");
},
Err(e) => return Err(e),
} Prevention
- Only pane_id is required; do not over-validate and reject valid directionless moves.
- Resolve pane ids from a pane-info query rather than hardcoding.
- Keep a typed list of which action variants require which fields and assert on it in tests.
When it happens
Trigger: Sending ActionType::MovePaneByPaneId where MovePaneByPaneIdAction.pane_id is None (with or without direction set).
Common situations: Automation moving panes programmatically; plugins hand-assembling move actions; client/server schema version skew; confusing with the focused-pane MovePane variant.
Related errors
- MovePaneBackwardsByPaneId missing pane_id
- TogglePanePinnedByPaneId missing pane_id
- PageScrollUpByPaneId missing pane_id
- PageScrollDownByPaneId missing pane_id
- HalfPageScrollUpByPaneId missing pane_id
AI-assisted analysis of zellij-org/zellij@98a0837077 (2026-08-16).
Data as JSON: /api/errors/50587fdb6f8a3ec7.
Report an issue: GitHub.