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.
Solutions
- 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.
- Validate key.len() == S at the API boundary and return an error instead of reaching try_into.
- Avoid hardcoded S; make it a computed const from the number of group-by fields so it can never diverge.
- 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
- Derive S from the group-by field count, never hardcode it
- Build keys through a single shared constructor
- Assert key arity in debug builds and tests
- Reject mismatched field lists at aggregation setup
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
- could not convert to String
- dictionary missing for str accessor
- could not convert to String
- interval not precomputed for date histogram source
- non-zero packed bits fit in u64
AI-assisted analysis of quickwit-oss/tantivy@b5d8deb80c (2026-09-05).
Data as JSON: /api/errors/746e54e9b0c4e9a8.
Report an issue: GitHub.
Appendix: 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)