{"record":{"id":"af63594d882fec8c","repo":"zellij-org/zellij","slug":"pagescrollupbypaneid-missing-pane-id","errorCode":null,"errorMessage":"PageScrollUpByPaneId missing pane_id","messagePattern":"PageScrollUpByPaneId missing pane_id","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"zellij-utils/src/ipc/protobuf_conversion.rs","lineNumber":2946,"sourceCode":"                    pane_id: a\n                        .pane_id\n                        .ok_or_else(|| anyhow!(\"ScrollToTopByPaneId missing pane_id\"))?\n                        .try_into()?,\n                })\n            },\n            ActionType::ScrollToBottomByPaneId(a) => {\n                Ok(crate::input::actions::Action::ScrollToBottomByPaneId {\n                    pane_id: a\n                        .pane_id\n                        .ok_or_else(|| anyhow!(\"ScrollToBottomByPaneId missing pane_id\"))?\n                        .try_into()?,\n                })\n            },\n            ActionType::PageScrollUpByPaneId(a) => {\n                Ok(crate::input::actions::Action::PageScrollUpByPaneId {\n                    pane_id: a\n                        .pane_id\n                        .ok_or_else(|| anyhow!(\"PageScrollUpByPaneId missing pane_id\"))?\n                        .try_into()?,\n                })\n            },\n            ActionType::PageScrollDownByPaneId(a) => {\n                Ok(crate::input::actions::Action::PageScrollDownByPaneId {\n                    pane_id: a\n                        .pane_id\n                        .ok_or_else(|| anyhow!(\"PageScrollDownByPaneId missing pane_id\"))?\n                        .try_into()?,\n                })\n            },\n            ActionType::HalfPageScrollUpByPaneId(a) => {\n                Ok(crate::input::actions::Action::HalfPageScrollUpByPaneId {\n                    pane_id: a\n                        .pane_id\n                        .ok_or_else(|| anyhow!(\"HalfPageScrollUpByPaneId missing pane_id\"))?\n                        .try_into()?,\n                })","sourceCodeStart":2928,"sourceCodeEnd":2964,"githubUrl":"https://github.com/zellij-org/zellij/blob/98a0837077492d53dd252ab30bc3e43e41e504f4/zellij-utils/src/ipc/protobuf_conversion.rs#L2928-L2964","documentation":"Zellij converts incoming protobuf IPC actions into its internal Action enum via TryFrom. The PageScrollUpByPaneId variant targets one specific pane, so the protobuf message PageScrollUpByPaneIdAction 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, the conversion aborts with 'PageScrollUpByPaneId missing pane_id' and the action is dropped before it reaches the tab/pane layer.","triggerScenarios":"An IPC ClientToServerMsg whose action.action_type oneof is set to PageScrollUpByPaneId while the inner PageScrollUpByPaneIdAction.pane_id is left None - e.g. building the prost struct with ::default() and forgetting the field, or a custom client/plugin that never populates it.","commonSituations":"Custom tooling or plugins that hand-assemble Action messages; version skew between a zellij client and server whose generated prost contracts differ; intending the focused-pane variant PageScrollUp (no pane_id required) but sending the ByPaneId one.","solutions":["Set the field before sending: pane_id: Some(PaneId { pane_type: Some(PaneType::Terminal(id)) }) (or PaneType::Plugin(id) for plugin panes).","If you meant the focused pane, send the plain PageScrollUp variant instead, which needs no pane_id.","Make sure the client, server, and any plugins are built against the same zellij release so the protobuf contract matches.","On the receiving side, log the serialized action and sender to identify which client omits pane_id."],"exampleFix":"// before\nlet action = ActionType::PageScrollUpByPaneId(PageScrollUpByPaneIdAction {\n    pane_id: None, // -> \"PageScrollUpByPaneId missing pane_id\"\n});\n\n// after\nlet action = ActionType::PageScrollUpByPaneId(PageScrollUpByPaneIdAction {\n    pane_id: Some(PaneId {\n        pane_type: Some(PaneType::Terminal(1)),\n    }),\n});","handlingStrategy":"validation","validationCode":"fn is_sendable(a: &PageScrollUpByPaneIdAction) -> bool {\n    a.pane_id\n        .as_ref()\n        .and_then(|p| p.pane_type.as_ref())\n        .is_some()\n}","typeGuard":"fn has_valid_pane_id(a: &PageScrollUpByPaneIdAction) -> bool {\n    matches!(\n        a.pane_id.as_ref().and_then(|p| p.pane_type.as_ref()),\n        Some(pane_id::PaneType::Terminal(_)) | Some(pane_id::PaneType::Plugin(_))\n    )\n}","tryCatchPattern":"match zellij_utils::input::actions::Action::try_from(proto_action) {\n    Ok(action) => dispatch(action),\n    Err(e) if e.to_string().contains(\"missing pane_id\") => {\n        log::warn!(\"dropping action with missing pane_id: {e}\");\n    },\n    Err(e) => return Err(e),\n}","preventionTips":["Never rely on prost default() for ByPaneId actions - the optional pane_id will be None.","Centralize action construction in one builder that takes pane_id: PaneId (not Option) as a parameter.","Round-trip test every action variant you emit: Action -> proto -> Action."],"tags":["zellij","ipc","protobuf","rust","pane-id","scroll"],"backgroundTag":null,"analyzedSha":"98a0837077492d53dd252ab30bc3e43e41e504f4","analyzedAt":"2026-08-16T13:02:01.396Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}