{"record":{"id":"16dcfa462640f80a","repo":"risingwavelabs/risingwave","slug":"node-cannot-be-convert-to-batch-node","errorCode":null,"errorMessage":"Node {} cannot be convert to batch node","messagePattern":"Node (.+?) cannot be convert to batch node","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/optimizer/plan_node/to_prost.rs","lineNumber":25,"sourceCode":"//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nuse anyhow::anyhow;\nuse risingwave_pb::batch_plan::plan_node as pb_batch_node;\nuse risingwave_pb::stream_plan::stream_node as pb_stream_node;\n\nuse super::*;\n\npub trait TryToBatchPb {\n    fn try_to_batch_prost_body(&self) -> SchedulerResult<pb_batch_node::NodeBody> {\n        // Originally we panic in the following way\n        // panic!(\"convert into distributed is only allowed on batch plan\")\n        Err(anyhow!(\n            \"Node {} cannot be convert to batch node\",\n            std::any::type_name::<Self>()\n        )\n        .into())\n    }\n}\n\npub trait ToBatchPb {\n    fn to_batch_prost_body(&self) -> pb_batch_node::NodeBody;\n}\n\nimpl<T: ToBatchPb> TryToBatchPb for T {\n    fn try_to_batch_prost_body(&self) -> SchedulerResult<pb_batch_node::NodeBody> {\n        Ok(self.to_batch_prost_body())\n    }\n}\n\npub trait TryToStreamPb {","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/optimizer/plan_node/to_prost.rs#L7-L43","documentation":"TryToBatchPb::try_to_batch_prost_body is a default trait method that always returns this error: it exists so that only batch plan nodes can be converted to a protobuf BatchNode body. Calling it on a non-batch (stream) plan node hits the default implementation, which converts the original panic into a SchedulerResult error.","triggerScenarios":"Calling try_to_batch_prost_body() on a plan node that has not overridden the trait method (any stream-only or generic plan node), typically during distributed batch query planning when the optimizer hands a stream plan to the batch plan-to-prost serializer.","commonSituations":"A developer adds a new plan node but forgets to implement TryToBatchPb; an internal bug routes a stream plan through the batch scheduler path.","solutions":["Verify the code path is converting a batch plan, not a stream plan, to prost.","Implement TryToBatchPb for the concrete plan node type (add a try_to_batch_prost_body override).","If this is unexpected at runtime, fix the optimizer dispatch so batch plans reach ToBatchProst and stream plans reach ToStreamProst."],"exampleFix":"// before\n// no impl, falls into default error\nimpl TryToBatchPb for MyJoinNode {}\n// after\nimpl TryToBatchPb for MyJoinNode {\n    fn try_to_batch_prost_body(&self) -> SchedulerResult<pb_batch_node::NodeBody> {\n        Ok(pb_batch_node::NodeBody::HashJoin(self.to_batch_hash_join_prost()?))\n    }\n}","handlingStrategy":"try-catch","validationCode":"// ensure node kind is batch before conversion\nif !matches!(plan_node, PlanNode::Batch(_)) { return Err(anyhow!(\"expected batch plan node\")); }","typeGuard":"fn as_batch(node: &PlanRef) -> Option<&BatchPlanNode> { node.as_batch() }","tryCatchPattern":"match node.try_to_batch_prost_body() {\n    Ok(body) => body,\n    Err(e) if e.to_string().contains(\"cannot be convert to batch node\") => fallback_to_stream_pipeline_or_bug_report(e),\n    Err(e) => return Err(e),\n}","preventionTips":["Implement TryToBatchPb for every new batch plan node type.","Add a test converting each node type to prost on both batch and stream paths.","Keep batch and stream planning paths clearly separated in the optimizer."],"tags":["frontend","plan-serialization","protobuf"],"backgroundTag":"unsupported-operation","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}