quickwit-oss/quickwit · error
The left iterator should not be empty.
Error message
The left iterator should not be empty.
What it means
In SortedDiffIterator::next, when both iterators peek as Some and left < right, the code calls self.left.next() and asserts it returns an item. Since peek() already returned Some, this can only fail if the underlying iterator misbehaves (returns Some on peek but None on next) — a violation of the Iterator contract.
Solutions
- Ensure the left iterator faithfully implements Iterator (peek(Some) implies next() yields an item)
- Do not mutate the underlying collection or the iterator while diffing
- Check for concurrent access; SortedIterator is not synchronization — wrap in a lock if shared
- If it persists, file/regress-fix in quickwit-common::sorted_iter
Example fix
// before
let left = self.left.next().expect("The left iterator should not be empty.");
// after
let left = match self.left.next() { Some(l) => l, None => return None }; // degrade gracefully instead of panicking Defensive patterns
Strategy: type-guard
Validate before calling
// Validate iterator contract before diffing: drain into a Vec first let left_items: Vec<_> = left.collect(); // then rebuild iterator assert_eq!(left_items.len(), /* expected */ left_items.len());
Type guard
fn is_consistent<I: Iterator + Clone>(it: &I) -> bool { it.clone().peekable().peek().is_some() } Try / catch
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| diff.for_each(|d| process(d)))).unwrap_or_else(|_| eprintln!("diff failed: iterator contract violation")); Prevention
- Only pass well-formed iterators (peek(Some) implies next yields) into SortedIterator
- Snapshot collections into Vecs before diffing live data
- Never mutate the underlying collection during the diff
- Do not share the iterator across threads without synchronization
When it happens
Trigger: Iterating a SortedIterator diff where the left iterator's peek() and next() disagree — only possible with a buggy/hand-rolled iterator that does not honor peek semantics, or interior mutation of the iterator during iteration.
Common situations: Custom Peekable-wrapped iterators that mutate shared state in next(); concurrent modification of the underlying collection while diffing; regression in sorted_iter.rs itself.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- The right iterator should not be empty.
- More memory released than allocated, should never happen.
- storage factory and config backends should match
- subscription map should exist
- 1 is always non-zero.
AI-assisted analysis of quickwit-oss/quickwit@a39730c5cd (2026-09-08).
Data as JSON: /api/errors/5b89ec42d496a4ca.
Report an issue: GitHub.
Appendix: source
Thrown at quickwit/quickwit-common/src/sorted_iter.rs:62
right: Peekable<U>,
}
impl<T, U, K> Iterator for DiffIterator<T, U>
where
T: Iterator<Item = K>,
U: Iterator<Item = K>,
K: Ord,
{
type Item = Diff<K>;
fn next(&mut self) -> Option<Self::Item> {
match (self.left.peek(), self.right.peek()) {
(Some(left), Some(right)) => match left.cmp(right) {
Ordering::Less => {
let left = self
.left
.next()
.expect("The left iterator should not be empty.");
Some(Diff::Removed(left))
}
Ordering::Equal => {
let left = self
.left
.next()
.expect("The left iterator should not be empty.");
self.right.next();
Some(Diff::Unchanged(left))
}
Ordering::Greater => {
let right = self
.right
.next()
.expect("The right iterator should not be empty.");
Some(Diff::Added(right))
}
},View on GitHub (pinned to a39730c5cd)