zellij-org/zellij · error · anyhow::Error
PageScrollDownByPaneId missing pane_id
Error message
PageScrollDownByPaneId missing pane_id
What it means
Zellij converts incoming protobuf IPC actions into its internal Action enum via TryFrom. The PageScrollDownByPaneId variant pages down the scrollback of one specific pane, so the protobuf message PageScrollDownByPaneIdAction must carry the optional field pane_id (a PaneId oneof: Terminal(u32) or Plugin(u32)). If the variant is selected but pane_id is None, conversion fails with 'PageScrollDownByPaneId missing pane_id' and the action never executes.
Source
Thrown at zellij-utils/src/ipc/protobuf_conversion.rs:2954
pane_id: a
.pane_id
.ok_or_else(|| anyhow!("ScrollToBottomByPaneId missing pane_id"))?
.try_into()?,
})
},
ActionType::PageScrollUpByPaneId(a) => {
Ok(crate::input::actions::Action::PageScrollUpByPaneId {
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()?,
})View on GitHub (pinned to 98a0837077)
Solutions
- Populate pane_id with Some(PaneId { pane_type: Some(PaneType::Terminal(id)) }) before sending.
- Use the plain PageScrollDown variant when targeting the focused pane.
- Align client, server, and plugin builds on the same zellij version.
- Add an Action -> proto -> Action round-trip test for every variant you send.
Example fix
// before
let action = ActionType::PageScrollDownByPaneId(PageScrollDownByPaneIdAction {
pane_id: None, // -> "PageScrollDownByPaneId missing pane_id"
});
// after
let action = ActionType::PageScrollDownByPaneId(PageScrollDownByPaneIdAction {
pane_id: Some(PaneId {
pane_type: Some(PaneType::Terminal(1)),
}),
}); Defensive patterns
Strategy: validation
Validate before calling
fn is_sendable(a: &PageScrollDownByPaneIdAction) -> bool {
a.pane_id
.as_ref()
.and_then(|p| p.pane_type.as_ref())
.is_some()
} Type guard
fn has_valid_pane_id(a: &PageScrollDownByPaneIdAction) -> 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("PageScrollDownByPaneId missing pane_id") => {
log::warn!("incomplete scroll action from client: {e}");
},
Err(e) => return Err(e),
} Prevention
- Prefer the focused-pane variant (PageScrollDown) when you hold no concrete pane id.
- Validate pane ids resolve to live panes before constructing ByPaneId actions.
- Keep client and server protobuf contracts version-locked.
When it happens
Trigger: Sending an action message whose action_type is PageScrollDownByPaneId while PageScrollDownByPaneIdAction.pane_id is None - typically a prost struct built with default() or a client that only fills the oneof discriminator and not the payload.
Common situations: Custom clients or plugins assembling Action messages by hand; zellij client/server version mismatch where the sender's schema lacks the pane_id field; confusion with the focused-pane variant PageScrollDown which needs no pane_id.
Related errors
- PageScrollUpByPaneId missing pane_id
- HalfPageScrollUpByPaneId 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/4d475f284b9c2032.
Report an issue: GitHub.