risingwavelabs/risingwave · error

GetChannelDeltaStatsExecutor should have no child!

Error message

GetChannelDeltaStatsExecutor should have no child!

What it means

During `deserialize_stream_chunk`, the number of decoded operations exceeded the `size_bound` the caller supplied for the chunk. `size_bound` caps how many rows a serialized chunk may contain so a maliciously or erroneously oversized payload cannot blow up memory; exceeding it aborts the decode.

Source

Thrown at src/batch/executors/src/executor/get_channel_delta_stats.rs:137

            let columns: Vec<_> = array_builders
                .into_iter()
                .map(|b| b.finish().into())
                .collect();

            let chunk = DataChunk::new(columns, rows.len());
            yield chunk;
        }
    }
}

impl BoxedExecutorBuilder for GetChannelDeltaStatsExecutor {
    async fn new_boxed_executor(
        source: &ExecutorBuilder<'_>,
        inputs: Vec<BoxedExecutor>,
    ) -> Result<BoxedExecutor> {
        ensure!(
            inputs.is_empty(),
            "GetChannelDeltaStatsExecutor should have no child!"
        );

        let get_channel_delta_stats_node = try_match_expand!(
            source.plan_node().get_node_body().unwrap(),
            NodeBody::GetChannelDeltaStats
        )?;

        // Create a schema for channel stats
        // This should match the expected schema from table_function.rs
        let fields = vec![
            Field::new("upstream_fragment_id", DataType::Int32),
            Field::new("downstream_fragment_id", DataType::Int32),
            Field::new("backpressure_rate", DataType::Float64),
            Field::new("recv_throughput", DataType::Float64),
            Field::new("send_throughput", DataType::Float64),
        ];

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Ensure the write side never serializes more rows per chunk than the configured `size_bound`.
  2. Fix the read range selection so `[start_seq_id, end_seq_id)` covers at most one chunk's rows.
  3. Increase size_bound if the workload legitimately needs larger chunks (and align it with writer settings).
  4. Inspect the seq-id range and stored rows to detect duplicated or miswritten entries.
Defensive patterns

Strategy: validation

Validate before calling

// rust
// estimate ops in the requested range before decoding
let est = reader.estimate_row_count(start_seq_id, end_seq_id);
anyhow::ensure!(est <= size_bound, "range [{}, {}) holds {} ops > size_bound {}", start_seq_id, end_seq_id, est, size_bound);

Prevention

When it happens

Trigger: Decoding a `[start_seq_id, end_seq_id)` range whose stored `LogStoreOp::Row` entries total more than `size_bound` ops (checked after each `ops.push(op)`). Thrown at serde.rs:472.

Common situations: Read range spanning more rows than estimated (e.g. start/end seq ids computed from a bad row-count estimate); rows written without respecting the chunk size bound; replaying a range twice because seq-id tracking desynced.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/1d93130f34448026. Report an issue: GitHub.