pola-rs/polars · error
itertools: .zip_eq() reached end of one iterator before the…
Error message
itertools: .zip_eq() reached end of one iterator before the other
What it means
Polars' ZipEq iterator panics when one of the two zipped iterators is exhausted before the other, i.e. they have unequal lengths. Unlike std::iter::zip (which silently stops at the shorter iterator), zip_eq asserts both sides have equal length, since unequal lengths indicate a broken invariant in the calling code.
Solutions
- Identify the two iterators being zipped and verify their lengths are equal before iteration
- If unequal lengths are legitimate, use std::iter::zip instead of zip_eq and handle the short side
- Report/fix the upstream invariant violation if this comes from within Polars itself (often a bug — check the issue tracker)
Example fix
// before
assert_eq!(a.len(), b.len(), "zip_eq inputs differ");
let pairs: Vec<_> = a.iter().zip_eq(b.iter()).collect();
// after
if a.len() != b.len() {
let pairs: Vec<_> = a.iter().zip(b.iter()).collect(); // tolerate short side
} else {
let pairs: Vec<_> = a.iter().zip_eq(b.iter()).collect();
} Defensive patterns
Strategy: type-guard
Validate before calling
// Rust: assert equal lengths before zipping assert_eq!(a.len(), b.len(), "zip_eq requires equal-length iterators");
Type guard
fn equal_len<A, B>(a: &[A], b: &[B]) -> bool { a.len() == b.len() } Prevention
- Prefer std::iter::zip when unequal lengths are possible and tolerable
- Check collection lengths at construction time, not iteration time
- Keep polars crate versions in lockstep to avoid schema/chunk skew
When it happens
Trigger: Any use of zip_eq (from polars-utils' itertools) where the two iterators produce different numbers of elements — e.g. zipping columns of a frame that have different lengths, or chunk lists built inconsistently.
Common situations: Internal invariant bugs where two collections are assumed parallel but constructed differently; plugin/extension code zipping field lists against array lists after a schema change; version-skew between polars crates.
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
- cross join filter holds no keys
- activate dtype
- addition overflow: +
- can only use timedelta string language with…
- cannot coerce datatypes
AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18).
Data as JSON: /api/errors/2d8c1c15087d0252.
Report an issue: GitHub.
Appendix: source
Thrown at crates/polars-utils/src/itertools/zip_eq.rs:20
/// An iterator which iterates two other iterators simultaneously
/// and panic if they have different lengths.
#[derive(Clone, Debug)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct ZipEq<I, J> {
a: I,
b: J,
}
impl<I: Iterator, J: Iterator> Iterator for ZipEq<I, J> {
type Item = (I::Item, J::Item);
fn next(&mut self) -> Option<Self::Item> {
match (self.a.next(), self.b.next()) {
(None, None) => None,
(Some(a), Some(b)) => Some((a, b)),
(None, Some(_)) | (Some(_), None) => {
panic!("itertools: .zip_eq() reached end of one iterator before the other")
},
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (a_lower, a_upper) = self.a.size_hint();
let (b_lower, b_upper) = self.b.size_hint();
let lower = cmp::min(a_lower, b_lower);
let upper = match (a_upper, b_upper) {
(Some(u1), Some(u2)) => Some(cmp::min(u1, u2)),
_ => a_upper.or(b_upper),
};
(lower, upper)
}
}
impl<I: ExactSizeIterator, J: ExactSizeIterator> ExactSizeIterator for ZipEq<I, J> {}
View on GitHub (pinned to fe841f959e)