{"record":{"id":"859278dc04f74b31","repo":"databendlabs/databend","slug":"dataexchange-merge-unreachable","errorCode":null,"errorMessage":"DataExchange::Merge(_) => unreachable!()","messagePattern":"DataExchange::Merge\\(_\\) => unreachable!\\(\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/query/service/src/pipelines/processors/transforms/aggregator/aggregate_exchange_injector.rs","lineNumber":64,"sourceCode":"        params: Arc<AggregatorParams>,\n        shuffle_mode: AggregateShuffleMode,\n    ) -> Arc<dyn ExchangeInjector> {\n        Arc::new(AggregateInjector {\n            ctx,\n            aggregator_params: params,\n            shuffle_mode,\n        })\n    }\n}\n\nimpl ExchangeInjector for AggregateInjector {\n    fn flight_scatter(\n        &self,\n        _: &Arc<QueryContext>,\n        exchange: &DataExchange,\n    ) -> Result<Arc<Box<dyn FlightScatter>>> {\n        match exchange {\n            DataExchange::Merge(_) => unreachable!(),\n            DataExchange::Broadcast(_) => unreachable!(),\n            DataExchange::GlobalShuffleExchange(_) => unreachable!(),\n            DataExchange::NodeToNodeExchange(exchange) => match self.shuffle_mode {\n                AggregateShuffleMode::Row => Ok(Arc::new(Box::new(AggregateRowScatter {\n                    buckets: exchange.destination_ids.len(),\n                    aggregate_params: self.aggregator_params.clone(),\n                }))),\n                AggregateShuffleMode::Bucket(_) => Ok(Arc::new(Box::new(AggregateBucketScatter {\n                    buckets: exchange.destination_ids.len(),\n                }))),\n            },\n        }\n    }\n\n    fn exchange_sorting(&self) -> Option<Arc<dyn ExchangeSorting>> {\n        None\n    }\n","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/query/service/src/pipelines/processors/transforms/aggregator/aggregate_exchange_injector.rs#L46-L82","documentation":"`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.","triggerScenarios":"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.","commonSituations":"Query-planning bugs where the optimizer misclassifies the exchange type for a distributed aggregation; regressions in exchange injection logic after planner changes.","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"],"exampleFix":"// before\nDataExchange::Merge(_) => unreachable!(),\n// after\nDataExchange::Merge(_) => Err(ErrorCode::Unimplemented(\"aggregate shuffle does not support Merge exchange\")),","handlingStrategy":"validation","validationCode":"if !matches!(exchange, DataExchange::NodeToNodeExchange(_)) { return Err(ErrorCode::Unimplemented(\"aggregate injection requires NodeToNodeExchange\")); }","typeGuard":"fn is_node_to_node(e: &DataExchange) -> bool { matches!(e, DataExchange::NodeToNodeExchange(_)) }","tryCatchPattern":null,"preventionTips":["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"],"tags":["rust","panic","query-planning","distributed"],"backgroundTag":"internal-invariant-violation","analyzedSha":"288d84d76e20a2f8f7173bda9691eb6ece301aa9","analyzedAt":"2026-09-11T11:29:36.208Z","contentChangedAt":"2026-09-11T11:29:36.208Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}