pola-rs/polars · error

Length to be non-zero

Error message

Length to be non-zero

What it means

This panic comes from `OffsetsBuffer::try_extend_from_slice` in polars-arrow, which slices `other.0[start..start + length + 1]` and calls `.last().expect("Length to be non-zero")` on the result. It means the sliced region of the offsets buffer was empty, so the code could not read the final offset needed to compute the appended length. The library treats a non-empty sliced offsets region as an internal invariant and panics rather than returning a `PolarsResult` error.

Solutions

  1. Validate that `other.0.len() >= start + length + 1` and that the sliced region is non-empty before calling try_extend_from_slice.
  2. Check that the source offsets buffer was produced by a trusted/validated constructor (e.g. OffsetsBuffer::try_from) rather than raw memory reinterpretation.
  3. Downgrade to a guarded path: if the slice would be empty, return early instead of extending.
  4. Verify array/IPC/Parquet producer and consumer versions match so offset buffers have the expected layout.

Example fix

// before
let other = &other.0[start..start + length + 1];
let other_length = other.last().expect("Length to be non-zero");
// after
let other = other.0.get(start..start + length + 1)
    .ok_or_else(|| polars_err!(ComputeError: "offsets slice out of bounds"))?;
let other_length = other.last()
    .ok_or_else(|| polars_err!(ComputeError: "empty offsets slice"))?;
Defensive patterns

Strategy: validation

Validate before calling

fn can_extend(offsets: &[i64], start: usize, length: usize) -> bool {
    length > 0
        && start.checked_add(length).map(|e| e + 1).map_or(false, |end| end <= offsets.len())
        && !offsets.is_empty()
}

Type guard

fn has_final_offset(slice: &[i64]) -> bool { !slice.is_empty() }

Prevention

When it happens

Trigger: Calling `Offsets::try_extend_from_slice(buffer, start, length)` with arguments such that `&other.0[start..start+length+1]` is an empty slice — i.e., a corrupt/truncated offsets buffer, or `start`/`length` values that do not match the actual contents of the offsets buffer. Note that `length == 0` short-circuits earlier, so only `length >= 1` with an empty or out-of-bounds-mapped slice region reaches the expect.

Common situations: Building list/binary view arrays by slicing another array's offsets with hand-computed start/length pairs; deserializing Arrow IPC or Parquet data whose offsets buffer was truncated or mis-declared; version mismatches where offset buffers were constructed by code with different invariants.

Related errors


AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18). Data as JSON: /api/errors/260653e144c14300. Report an issue: GitHub.

Appendix: source

Thrown at crates/polars-arrow/src/offset.rs:315

        });
        self.0.extend(offsets);
        Ok(())
    }

    /// Extends itself from another [`Offsets`] sliced by `start, length`
    /// # Errors
    /// This function errors iff this operation overflows for the maximum value of `O`.
    pub fn try_extend_from_slice(
        &mut self,
        other: &OffsetsBuffer<O>,
        start: usize,
        length: usize,
    ) -> PolarsResult<()> {
        if length == 0 {
            return Ok(());
        }
        let other = &other.0[start..start + length + 1];
        let other_length = other.last().expect("Length to be non-zero");
        let mut length = *self.last();
        // check if the operation would overflow
        length
            .checked_add(other_length)
            .ok_or_else(|| polars_err!(ComputeError: "overflow"))?;

        let lengths = other.windows(2).map(|w| w[1] - w[0]);
        let offsets = lengths.map(|new_length| {
            length += new_length;
            length
        });
        self.0.extend(offsets);
        Ok(())
    }

    /// Returns the inner [`Vec`].
    #[inline]
    pub fn into_inner(self) -> Vec<O> {

View on GitHub (pinned to fe841f959e)