{"record":{"id":"127d0c7d10f96f33","repo":"quickwit-oss/tantivy","slug":"unexpected-aggregation-expected-histogram-aggrega","errorCode":null,"errorMessage":"unexpected aggregation, expected histogram aggregation","messagePattern":"unexpected aggregation, expected histogram aggregation","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/aggregation/intermediate_agg_result.rs","lineNumber":633,"sourceCode":"}\n\nimpl IntermediateBucketResult {\n    pub(crate) fn into_final_bucket_result(\n        self,\n        req: &Aggregation,\n        limits: &mut AggregationLimitsGuard,\n    ) -> crate::Result<BucketResult> {\n        match self {\n            IntermediateBucketResult::Range(range_res) => {\n                let mut buckets: Vec<RangeBucketEntry> = range_res\n                    .buckets\n                    .into_values()\n                    .map(|bucket| {\n                        bucket.into_final_bucket_entry(\n                            req.sub_aggregation(),\n                            req.agg\n                                .as_range()\n                                .expect(\"unexpected aggregation, expected histogram aggregation\"),\n                            range_res.column_type,\n                            limits,\n                        )\n                    })\n                    .collect::<crate::Result<Vec<_>>>()?;\n\n                buckets.sort_by(|left, right| {\n                    left.from\n                        .unwrap_or(f64::MIN)\n                        .total_cmp(&right.from.unwrap_or(f64::MIN))\n                });\n\n                let is_keyed = req\n                    .agg\n                    .as_range()\n                    .expect(\"unexpected aggregation, expected range aggregation\")\n                    .keyed;\n                let buckets = if is_keyed {","sourceCodeStart":615,"sourceCodeEnd":651,"githubUrl":"https://github.com/quickwit-oss/tantivy/blob/b5d8deb80c26924e6b007a5b1a7630f35ca64de4/src/aggregation/intermediate_agg_result.rs#L615-L651","documentation":"A panic in `into_final_bucket_result` while finalizing a range aggregation's buckets: the code expects `req.agg.as_range()` to succeed, with a (confusingly worded) message saying \"expected histogram aggregation\". The panic means the intermediate bucket result is a range-type result but the request's aggregation definition is not a range aggregation, so the request and the intermediate result disagree on aggregation type.","triggerScenarios":"Finalizing an intermediate range bucket result where the request's agg (looked up by aggregation id/path) resolves to a non-range aggregation, e.g. due to mismatched aggregation ids in a distributed merge or a rebuilt request tree.","commonSituations":"Coordinator merging shard results whose aggregation trees differ (version skew); caching intermediate results under wrong keys; programmatically mutating the aggregation request after intermediate results were produced.","solutions":["Use the original, unchanged request object when calling into_final_bucket_result.","Ensure all cluster nodes use the same engine version so agg ids resolve identically.","Avoid reordering/renaming aggregations between the search request and the finalization pass.","If you maintain the code, replace expect with ok_or to return a typed internal error."],"exampleFix":"// before\nreq.agg.as_range().expect(\"unexpected aggregation, expected histogram aggregation\")\n// after\nreq.agg.as_range()\n    .ok_or_else(|| crate::AggregationError::Internal(\"expected range agg\".to_string()))?","handlingStrategy":"validation","validationCode":"fn is_range_agg(req: &Aggregation) -> bool {\n    matches!(req, Aggregation::Range(_))\n}\n// check before calling into_final_bucket_result","typeGuard":"fn as_range(req: &Aggregation) -> Option<&RangeAgg> {\n    if let Aggregation::Range(r) = req { Some(r) } else { None }\n}","tryCatchPattern":"let res = std::panic::catch_unwind(|| into_final_bucket_result(intermediate, &req, limits));\nif res.is_err() {\n    return internal_error(\"range agg finalization: request/intermediate mismatch\");\n}","preventionTips":["Do not mutate or rebuild the aggregation tree between search and finalization.","Use one engine version cluster-wide.","Key any intermediate-result cache on the complete request payload."],"tags":["rust","panic","aggregation","type-mismatch","internal-invariant"],"backgroundTag":"aggregation-type-mismatch","analyzedSha":"b5d8deb80c26924e6b007a5b1a7630f35ca64de4","analyzedAt":"2026-09-05T13:20:51.521Z","contentChangedAt":"2026-09-05T13:20:51.521Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}