influxdata/influxdb · error

should have a single merged statistic

Error message

should have a single merged statistic

What it means

Panics via `.expect("should have a single merged statistic")` in RepartitionExec::statistics_by_partition. merge_partitions_no_projection is expected to return exactly one merged Statistics entry for repartitioned input; if it returns zero entries, the pop panics. This signals that input partition statistics were never produced.

Solutions

  1. Verify the child executor of RepartitionExec reports statistics for its partitions
  2. Make merge_partitions_no_projection return an error instead of an empty vec when inputs have no statistics
  3. File a bug if this occurs with stock IOx/DataFusion plans

Example fix

// before
let merged_single = merged.pop().expect("should have a single merged statistic");
// after
let merged_single = merged.pop().ok_or_else(|| {
    internal_datafusion_err!("repartition input produced no merged statistics")
})?;
Defensive patterns

Strategy: try-catch

Validate before calling

if child.statistics()?.partition_statistics.is_empty() { /* skip repartition stats path */ }

Try / catch

// wrap statistics_by_partition in catch_unwind for custom executors
let res = std::panic::catch_unwind(|| exec.statistics_by_partition());

Prevention

When it happens

Trigger: Calling statistics_by_partition on a RepartitionExec whose input yielded no partition statistics (empty merged vec), typically because the child executor's statistics are unavailable or the child plan has no partitions.

Common situations: Custom child executors returning empty statistics vectors; plans where repartition sits on top of a source that reports no row statistics; regressions after changing merge_partitions_no_projection.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19). Data as JSON: /api/errors/6320f072a16d6790. Report an issue: GitHub.

Appendix: source

Thrown at core/iox_query/src/statistics/partition_statistics/mod.rs:258

}

impl PartitionStatistics for SortExec {
    fn statistics_by_partition(&self) -> Result<PartitionedStatistics> {
        if self.preserve_partitioning() {
            preserve_partitioning_no_projection_apply_fetch(self, self.fetch(), 0)
        } else {
            merge_partitions_no_projection_apply_fetch(self, self.fetch(), 0)
        }
    }
}

impl PartitionStatistics for RepartitionExec {
    fn statistics_by_partition(&self) -> Result<PartitionedStatistics> {
        // takes N input partitions, returning M output partitions.

        // the statistics are "merged" into the same value (because any input can go to any output)
        let mut merged = merge_partitions_no_projection(self)?;
        let merged_single = merged.pop().expect("should have a single merged statistic");
        let merged_single = Arc::unwrap_or_clone(merged_single);

        // then the merged stat is turned to inexact (since unknown division across output partitions)
        let inexact_merged = Arc::new(Statistics {
            num_rows: Precision::Absent,
            total_byte_size: Precision::Absent,
            column_statistics: make_column_statistics_inexact(merged_single.column_statistics),
        });

        // finally, all output partitions have the same merged stat
        Ok(vec![inexact_merged; partition_count(self)])
    }
}

impl PartitionStatistics for AggregateExec {
    fn statistics_by_partition(&self) -> Result<PartitionedStatistics> {
        if self.aggr_expr().is_empty() {
            let inner_stats_per_partition = statistics_by_partition(self.input.as_ref())?;

View on GitHub (pinned to 06200ef96b)