quickwit-oss/tantivy · error

unexpected aggregation, expected composite aggregation

Error message

unexpected aggregation, expected composite aggregation

What it means

A panic in `into_final_bucket_result`: `IntermediateBucketResult::Composite` is finalized with `req.agg.as_composite().expect("unexpected aggregation, expected composite aggregation")`, which fires when the request's aggregation is not a composite aggregation. This is the same family of request/intermediate-result type mismatches: the intermediate bucket result claims composite but the request definition does not.

Source

Thrown at src/aggregation/intermediate_agg_result.rs:714

                limits,
            ),
            IntermediateBucketResult::Filter {
                doc_count,
                sub_aggregations,
            } => {
                // Convert sub-aggregation results to final format
                let final_sub_aggregations = sub_aggregations
                    .into_final_result(req.sub_aggregation().clone(), limits.clone())?;
                Ok(BucketResult::Filter(FilterBucketResult {
                    doc_count,
                    sub_aggregations: final_sub_aggregations,
                }))
            }
            IntermediateBucketResult::Composite { buckets } => {
                let composite_req = req
                    .agg
                    .as_composite()
                    .expect("unexpected aggregation, expected composite aggregation");
                buckets.into_final_result(composite_req, req.sub_aggregation(), limits)
            }
            IntermediateBucketResult::MultiTerms { buckets } => {
                let multi_terms_req = req
                    .agg
                    .as_multi_terms()
                    .expect("unexpected aggregation, expected multi_terms aggregation");
                buckets.into_final_result(multi_terms_req, req.sub_aggregation(), limits)
            }
        }
    }

    pub(crate) fn prune_intermediate_results(
        &mut self,
        req: &Aggregation,
        mode: PruneMode,
    ) -> crate::Result<()> {
        match self {

View on GitHub (pinned to b5d8deb80c)

Solutions

  1. Use the original request object for finalization; never reuse intermediates across requests.
  2. Keep all cluster nodes on the same engine version.
  3. Verify the aggregation tree sent to shards matches the one used by the coordinator for finalization.
  4. If you maintain the code, propagate an internal error via ok_or instead of expecting.

Example fix

// before
req.agg.as_composite().expect("unexpected aggregation, expected composite aggregation")
// after
req.agg.as_composite()
    .ok_or_else(|| crate::AggregationError::Internal("expected composite agg".to_string()))?
Defensive patterns

Strategy: validation

Validate before calling

fn is_composite_agg(req: &Aggregation) -> bool {
    matches!(req, Aggregation::Composite(_))
}
// check before finalizing composite intermediates

Type guard

fn as_composite(req: &Aggregation) -> Option<&CompositeAgg> {
    if let Aggregation::Composite(c) = req { Some(c) } else { None }
}

Try / catch

let res = std::panic::catch_unwind(|| buckets.into_final_result(composite_req, sub_agg, limits));
if res.is_err() {
    return internal_error("expected composite aggregation during finalization");
}

Prevention

When it happens

Trigger: Finalizing composite aggregation intermediate results when `req.agg` resolves to a non-composite aggregation — mismatched aggregation ids during distributed merging, version-skewed nodes, or intermediate results paired with the wrong request.

Common situations: Rolling upgrades where shard responses follow a different intermediate format; cache hits returning intermediates from another query; custom tooling rebuilding the aggregation request tree.

Related errors


AI-assisted analysis of quickwit-oss/tantivy@b5d8deb80c (2026-09-05). Data as JSON: /api/errors/891047e3be23e5dc. Report an issue: GitHub.