quickwit-oss/tantivy · error

Key length mismatch

Error message

Key length mismatch

What it means

ArrayHeapMap is a fixed-size-key map where every key is [K; S]; get_or_insert_with converts the incoming &[K] slice to &[K; S] with try_into().expect(...). If the caller passes a key slice whose length differs from the const generic S, the conversion fails and the process panics with "Key length mismatch". The doc comment explicitly states this panic condition.

Source

Thrown at src/aggregation/bucket/composite/map.rs:30

#[derive(Clone, Debug)]
struct ArrayHeapMap<K: Ord, V, const S: usize> {
    pub(crate) buckets: FxHashMap<[K; S], V>,
    pub(crate) heap: BinaryHeap<[K; S]>,
}

impl<K: Ord, V, const S: usize> Default for ArrayHeapMap<K, V, S> {
    fn default() -> Self {
        ArrayHeapMap {
            buckets: FxHashMap::default(),
            heap: BinaryHeap::default(),
        }
    }
}

impl<K: Eq + Hash + Clone + Ord, V, const S: usize> ArrayHeapMap<K, V, S> {
    /// Panics if the length of `key` is not S.
    fn get_or_insert_with<F: FnOnce() -> V>(&mut self, key: &[K], f: F) -> &mut V {
        let key_array: &[K; S] = key.try_into().expect("Key length mismatch");
        self.buckets.entry(key_array.clone()).or_insert_with(|| {
            self.heap.push(key_array.clone());
            f()
        })
    }

    /// Panics if the length of `key` is not S.
    fn get_mut(&mut self, key: &[K]) -> Option<&mut V> {
        let key_array: &[K; S] = key.try_into().expect("Key length mismatch");
        self.buckets.get_mut(key_array)
    }

    fn peek_highest(&self) -> Option<&[K]> {
        self.heap.peek().map(|k_array| k_array.as_slice())
    }

    fn evict_highest(&mut self) {
        if let Some(highest) = self.heap.pop() {

View on GitHub (pinned to b5d8deb80c)

Solutions

  1. Ensure the composite collector's key length always equals S: assert/derive S from the source field count and size the map with that constant.
  2. Validate key.len() == S at the API boundary and return an error instead of reaching try_into.
  3. Avoid hardcoded S; make it a computed const from the number of group-by fields so it can never diverge.
  4. Add a debug_assert!(key.len() == S) in tests covering all field-count configurations.

Example fix

// before
let key: &[K] = &dynamic_key; // length 3
map.get_or_insert_with(&key, default) // S == 2, panics
// after
assert_eq!(dynamic_key.len(), S, "composite key arity must match map width");
map.get_or_insert_with(&dynamic_key, default)
Defensive patterns

Strategy: validation

Validate before calling

// Validate key length against map width before insert
if key.len() != S {
    return Err(TantivyError::InvalidArgument(format!(
        "composite key length {} != expected {}", key.len(), S)));
}
map.get_or_insert_with(key, default);

Type guard

fn key_len_ok<K>(key: &[K], s: usize) -> bool { key.len() == s }

Prevention

When it happens

Trigger: Calling get_or_insert_with with a key slice of length != S — e.g. building composite bucket keys from a number of source fields different from the ArrayHeapMap's declared width S, or pushing an extra/missing key element.

Common situations: Composite aggregation over a variable number of fields while the heap map was sized for a fixed arity; field list built dynamically (empty field or extra field added) changing key length at runtime.

Related errors


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