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
- Dump the query plan (`EXPLAIN`) of the failing query and check which exchange was built for the aggregation
- Verify the planner/injector decides to inject aggregation only when the exchange is a `NodeToNodeExchange`
- Return a proper planner error (e.g. `ErrorCode::Unimplemented`) instead of `unreachable!()` for clarity
- 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
- Assert exchange kind before invoking AggregateExchangeInjector
- Add planner tests for each DataExchange variant against the injector
- Fail planning with an explicit error rather than a runtime panic
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
- DataExchange::Broadcast(_) => unreachable!()
- DataExchange::GlobalShuffleExchange(_) => unreachable!()
- grouping function must be rewritten in type_checker, but got
- AggregateMeta does not support exchanging between multiple…
- _ => unreachable!()
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)