zellij-org/zellij · error · anyhow::Error
HalfPageScrollUpByPaneId missing pane_id
Error message
HalfPageScrollUpByPaneId missing pane_id
What it means
Zellij converts incoming protobuf IPC actions into its internal Action enum via TryFrom. The HalfPageScrollUpByPaneId variant scrolls a specific pane up by half a page, so HalfPageScrollUpByPaneIdAction must carry the optional pane_id field (PaneId oneof: Terminal(u32) or Plugin(u32)). If the variant is chosen but pane_id is None, conversion stops with 'HalfPageScrollUpByPaneId missing pane_id'.
Source
Thrown at zellij-utils/src/ipc/protobuf_conversion.rs:2962
pane_id: a
.pane_id
.ok_or_else(|| anyhow!("PageScrollUpByPaneId missing pane_id"))?
.try_into()?,
})
},
ActionType::PageScrollDownByPaneId(a) => {
Ok(crate::input::actions::Action::PageScrollDownByPaneId {
pane_id: a
.pane_id
.ok_or_else(|| anyhow!("PageScrollDownByPaneId missing pane_id"))?
.try_into()?,
})
},
ActionType::HalfPageScrollUpByPaneId(a) => {
Ok(crate::input::actions::Action::HalfPageScrollUpByPaneId {
pane_id: a
.pane_id
.ok_or_else(|| anyhow!("HalfPageScrollUpByPaneId missing pane_id"))?
.try_into()?,
})
},
ActionType::HalfPageScrollDownByPaneId(a) => {
Ok(crate::input::actions::Action::HalfPageScrollDownByPaneId {
pane_id: a
.pane_id
.ok_or_else(|| anyhow!("HalfPageScrollDownByPaneId missing pane_id"))?
.try_into()?,
})
},
ActionType::ResizeByPaneId(a) => {
let resize_action = a
.resize_action
.ok_or_else(|| anyhow!("ResizeByPaneId missing resize_action"))?;
let resize = proto_i32_to_resize(resize_action.resize)?;
let direction = resize_action
.directionView on GitHub (pinned to 98a0837077)
Solutions
- Set pane_id: Some(PaneId { pane_type: Some(PaneType::Terminal(id)) }) on the message.
- Prefer HalfPageScrollUp when the focused pane is the target.
- Rebuild both sides of the IPC boundary from the same zellij tag.
- Validate the message with a helper before conversion and reject early with a clear error.
Example fix
// before
let action = ActionType::HalfPageScrollUpByPaneId(HalfPageScrollUpByPaneIdAction {
pane_id: None, // -> "HalfPageScrollUpByPaneId missing pane_id"
});
// after
let action = ActionType::HalfPageScrollUpByPaneId(HalfPageScrollUpByPaneIdAction {
pane_id: Some(PaneId {
pane_type: Some(PaneType::Plugin(3)),
}),
}); Defensive patterns
Strategy: validation
Validate before calling
fn is_sendable(a: &HalfPageScrollUpByPaneIdAction) -> bool {
a.pane_id
.as_ref()
.and_then(|p| p.pane_type.as_ref())
.is_some()
} Type guard
fn has_valid_pane_id(a: &HalfPageScrollUpByPaneIdAction) -> 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 scroll action: {e}");
},
Err(e) => return Err(e),
} Prevention
- Check pane_id presence at the boundary where you build the message, not after the error.
- Use named constructors like page_scroll_up(pane: PaneId) that make incomplete states unrepresentable.
- Test scroll actions for both terminal and plugin pane targets.
When it happens
Trigger: An IPC action whose action_type oneof is HalfPageScrollUpByPaneId while the inner pane_id is None - e.g. constructing the prost message with defaults, or a sender built from a stale generated contract.
Common situations: Plugins or automation tools that hand-build keybinding-equivalent actions; client/server version skew; intending HalfPageScrollUp (focused pane, no pane_id).
Related errors
- PageScrollUpByPaneId missing pane_id
- PageScrollDownByPaneId missing pane_id
- HalfPageScrollDownByPaneId missing pane_id
- ResizeByPaneId missing resize_action
- ResizeByPaneId missing pane_id
AI-assisted analysis of zellij-org/zellij@98a0837077 (2026-08-16).
Data as JSON: /api/errors/2b78544b2b1ad7ef.
Report an issue: GitHub.