pola-rs/polars · error

Could not 'unwrap_optional'. 'ZipValidity' iterator has no n

Error message

Could not 'unwrap_optional'. 'ZipValidity' iterator has no nulls.

What it means

The inverse of unwrap_required: unwrap_optional() only works on ZipValidity::Optional (an iterator yielding Option<T>). Calling it on a Required value — built for an array without any validity — panics because there is no null information to attach.

Source

Thrown at crates/polars-arrow/src/bitmap/utils/zip_validity.rs:214

impl<T, I, V> ZipValidity<T, I, V>
where
    I: Iterator<Item = T>,
    V: Iterator<Item = bool>,
{
    /// Unwrap into an iterator that has no null values.
    pub fn unwrap_required(self) -> I {
        match self {
            ZipValidity::Required(i) => i,
            _ => panic!("Could not 'unwrap_required'. 'ZipValidity' iterator has nulls."),
        }
    }

    /// Unwrap into an iterator that has null values.
    pub fn unwrap_optional(self) -> ZipValidityIter<T, I, V> {
        match self {
            ZipValidity::Optional(i) => i,
            _ => panic!("Could not 'unwrap_optional'. 'ZipValidity' iterator has no nulls."),
        }
    }
}

View on GitHub (pinned to 9b5d73fd00)

Solutions

  1. Branch on validity(): is_none() -> consume plain values; is_some() -> unwrap_optional()
  2. If Option items are required for both cases, map the Required iterator with .map(Some)
  3. Avoid unwrap_optional in generic code; keep the ZipValidity enum and match on it

Example fix

// before
let it = zip_validity(values, validity).unwrap_optional();

// after
let it = match zip_validity(values, validity) {
    ZipValidity::Optional(it) => it,
    ZipValidity::Required(it) => it.map(Some),
};
Defensive patterns

Strategy: validation

Validate before calling

fn has_nulls(arr: &dyn polars_arrow::array::Array) -> bool {
    arr.validity().is_some()
}
// only unwrap_optional when has_nulls(&arr); otherwise map items with Some

Type guard

fn can_unwrap_optional<T, I, V>(zv: &polars_arrow::bitmap::utils::ZipValidity<T, I, V>) -> bool {
    matches!(zv, polars_arrow::bitmap::utils::ZipValidity::Optional(_))
}

Prevention

When it happens

Trigger: Calling unwrap_optional() on the ZipValidity produced when validity is None, e.g. generic code that unconditionally treats every iterator as nullable.

Common situations: A single code path written for both nullable and dense arrays with the dense case forgotten; arrays produced by a kernel that guarantees no validity (freshly built, never filtered).

Related errors


AI-assisted analysis of pola-rs/polars@9b5d73fd00 (2026-08-19). Data as JSON: /api/errors/3cac6a99b389b75c. Report an issue: GitHub.