quickwit-oss/tantivy · error
unexpected aggregation, expected histogram aggregation
Error message
unexpected aggregation, expected histogram aggregation
What it means
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.
Source
Thrown at src/aggregation/intermediate_agg_result.rs:633
}
impl IntermediateBucketResult {
pub(crate) fn into_final_bucket_result(
self,
req: &Aggregation,
limits: &mut AggregationLimitsGuard,
) -> crate::Result<BucketResult> {
match self {
IntermediateBucketResult::Range(range_res) => {
let mut buckets: Vec<RangeBucketEntry> = range_res
.buckets
.into_values()
.map(|bucket| {
bucket.into_final_bucket_entry(
req.sub_aggregation(),
req.agg
.as_range()
.expect("unexpected aggregation, expected histogram aggregation"),
range_res.column_type,
limits,
)
})
.collect::<crate::Result<Vec<_>>>()?;
buckets.sort_by(|left, right| {
left.from
.unwrap_or(f64::MIN)
.total_cmp(&right.from.unwrap_or(f64::MIN))
});
let is_keyed = req
.agg
.as_range()
.expect("unexpected aggregation, expected range aggregation")
.keyed;
let buckets = if is_keyed {View on GitHub (pinned to b5d8deb80c)
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.
Example fix
// before
req.agg.as_range().expect("unexpected aggregation, expected histogram aggregation")
// after
req.agg.as_range()
.ok_or_else(|| crate::AggregationError::Internal("expected range agg".to_string()))? Defensive patterns
Strategy: validation
Validate before calling
fn is_range_agg(req: &Aggregation) -> bool {
matches!(req, Aggregation::Range(_))
}
// check before calling into_final_bucket_result Type guard
fn as_range(req: &Aggregation) -> Option<&RangeAgg> {
if let Aggregation::Range(r) = req { Some(r) } else { None }
} Try / catch
let res = std::panic::catch_unwind(|| into_final_bucket_result(intermediate, &req, limits));
if res.is_err() {
return internal_error("range agg finalization: request/intermediate mismatch");
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- unexpected metric type
- unexpected aggregation, expected range aggregation
- unexpected aggregation, expected term aggregation
- unexpected aggregation, expected composite aggregation
- TermMissingAgg collector, but no missing found in agg req
AI-assisted analysis of quickwit-oss/tantivy@b5d8deb80c (2026-09-05).
Data as JSON: /api/errors/127d0c7d10f96f33.
Report an issue: GitHub.