databendlabs/databend · error

DataExchange::Merge(_) => unreachable!()

Error message

DataExchange::Merge(_) => unreachable!()

What it means

`AggregateExchangeInjector::flight_scatter` panics via `unreachable!()` when asked to build a scatter strategy for a `DataExchange::Merge` exchange. Aggregate shuffle injection is only valid for `NodeToNodeExchange`; Merge exchanges imply a different (single-destination) plan shape that should never reach the aggregate injector.

Solutions

  1. Dump the query plan (`EXPLAIN`) of the failing query and check which exchange was built for the aggregation
  2. Verify the planner/injector decides to inject aggregation only when the exchange is a `NodeToNodeExchange`
  3. Return a proper planner error (e.g. `ErrorCode::Unimplemented`) instead of `unreachable!()` for clarity
  4. Update Databend — planner bugs around exchange injection are frequently patched

Example fix

// before
DataExchange::Merge(_) => unreachable!(),
// after
DataExchange::Merge(_) => Err(ErrorCode::Unimplemented("aggregate shuffle does not support Merge exchange")),
Defensive patterns

Strategy: validation

Validate before calling

if !matches!(exchange, DataExchange::NodeToNodeExchange(_)) { return Err(ErrorCode::Unimplemented("aggregate injection requires NodeToNodeExchange")); }

Type guard

fn is_node_to_node(e: &DataExchange) -> bool { matches!(e, DataExchange::NodeToNodeExchange(_)) }

Prevention

When it happens

Trigger: Calling `flight_scatter` (via the exchange planning code) with a `DataExchange::Merge` variant, i.e. the plan builder injected an aggregate shuffle into a merge-exchange context.

Common situations: Query-planning bugs where the optimizer misclassifies the exchange type for a distributed aggregation; regressions in exchange injection logic after planner changes.

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 databendlabs/databend@288d84d76e (2026-09-11). Data as JSON: /api/errors/859278dc04f74b31. Report an issue: GitHub.

Appendix: source

Thrown at src/query/service/src/pipelines/processors/transforms/aggregator/aggregate_exchange_injector.rs:64

        params: Arc<AggregatorParams>,
        shuffle_mode: AggregateShuffleMode,
    ) -> Arc<dyn ExchangeInjector> {
        Arc::new(AggregateInjector {
            ctx,
            aggregator_params: params,
            shuffle_mode,
        })
    }
}

impl ExchangeInjector for AggregateInjector {
    fn flight_scatter(
        &self,
        _: &Arc<QueryContext>,
        exchange: &DataExchange,
    ) -> Result<Arc<Box<dyn FlightScatter>>> {
        match exchange {
            DataExchange::Merge(_) => unreachable!(),
            DataExchange::Broadcast(_) => unreachable!(),
            DataExchange::GlobalShuffleExchange(_) => unreachable!(),
            DataExchange::NodeToNodeExchange(exchange) => match self.shuffle_mode {
                AggregateShuffleMode::Row => Ok(Arc::new(Box::new(AggregateRowScatter {
                    buckets: exchange.destination_ids.len(),
                    aggregate_params: self.aggregator_params.clone(),
                }))),
                AggregateShuffleMode::Bucket(_) => Ok(Arc::new(Box::new(AggregateBucketScatter {
                    buckets: exchange.destination_ids.len(),
                }))),
            },
        }
    }

    fn exchange_sorting(&self) -> Option<Arc<dyn ExchangeSorting>> {
        None
    }

View on GitHub (pinned to 288d84d76e)