risingwavelabs/risingwave · error
Node {} cannot be convert to batch node
Error message
Node {} cannot be convert to batch node What it means
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.
Source
Thrown at src/frontend/src/optimizer/plan_node/to_prost.rs:25
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use anyhow::anyhow;
use risingwave_pb::batch_plan::plan_node as pb_batch_node;
use risingwave_pb::stream_plan::stream_node as pb_stream_node;
use super::*;
pub trait TryToBatchPb {
fn try_to_batch_prost_body(&self) -> SchedulerResult<pb_batch_node::NodeBody> {
// Originally we panic in the following way
// panic!("convert into distributed is only allowed on batch plan")
Err(anyhow!(
"Node {} cannot be convert to batch node",
std::any::type_name::<Self>()
)
.into())
}
}
pub trait ToBatchPb {
fn to_batch_prost_body(&self) -> pb_batch_node::NodeBody;
}
impl<T: ToBatchPb> TryToBatchPb for T {
fn try_to_batch_prost_body(&self) -> SchedulerResult<pb_batch_node::NodeBody> {
Ok(self.to_batch_prost_body())
}
}
pub trait TryToStreamPb {View on GitHub (pinned to 6469eb736d)
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.
Example fix
// before
// no impl, falls into default error
impl TryToBatchPb for MyJoinNode {}
// after
impl TryToBatchPb for MyJoinNode {
fn try_to_batch_prost_body(&self) -> SchedulerResult<pb_batch_node::NodeBody> {
Ok(pb_batch_node::NodeBody::HashJoin(self.to_batch_hash_join_prost()?))
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure node kind is batch before conversion
if !matches!(plan_node, PlanNode::Batch(_)) { return Err(anyhow!("expected batch plan node")); } Type guard
fn as_batch(node: &PlanRef) -> Option<&BatchPlanNode> { node.as_batch() } Try / catch
match node.try_to_batch_prost_body() {
Ok(body) => body,
Err(e) if e.to_string().contains("cannot be convert to batch node") => fallback_to_stream_pipeline_or_bug_report(e),
Err(e) => return Err(e),
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: A developer adds a new plan node but forgets to implement TryToBatchPb; an internal bug routes a stream plan through the batch scheduler path.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Node {} cannot be convert to stream node
- query_epoch not set in distributed lookup join
- unspecified AsOf join inequality type
- no chunk in IngestDmlPayloadRequest
- Pb decode error: {0}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/16dcfa462640f80a.
Report an issue: GitHub.