quickwit-oss/tantivy · error · io::Error

InvalidData

InvalidData

Error message

GCD of 0 is forbidden

What it means

ColumnStats deserialization reads a GCD value that must be non-zero because it is wrapped in NonZeroU64 and multiplies the amplitude. A serialized gcd of 0 is invalid, so the reader returns InvalidData with this message. It signals corrupt or foreign-format stats data.

Source

Thrown at columnar/src/column_values/stats.rs:43

    pub fn amplitude(&self) -> u64 {
        self.max_value - self.min_value
    }
}

impl BinarySerializable for ColumnStats {
    fn serialize<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
        VInt(self.min_value).serialize(writer)?;
        VInt(self.gcd.get()).serialize(writer)?;
        VInt(self.amplitude() / self.gcd).serialize(writer)?;
        VInt(self.num_rows as u64).serialize(writer)?;
        Ok(())
    }

    fn deserialize<R: io::Read>(reader: &mut R) -> io::Result<Self> {
        let min_value = VInt::deserialize(reader)?.0;
        let gcd = VInt::deserialize(reader)?.0;
        let gcd = NonZeroU64::new(gcd)
            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "GCD of 0 is forbidden"))?;
        let amplitude = VInt::deserialize(reader)?.0 * gcd.get();
        let max_value = min_value + amplitude;
        let num_rows = VInt::deserialize(reader)?.0 as RowId;
        Ok(ColumnStats {
            min_value,
            max_value,
            num_rows,
            gcd,
        })
    }
}

#[cfg(test)]
mod tests {
    use std::num::NonZeroU64;

    use common::BinarySerializable;

View on GitHub (pinned to b5d8deb80c)

Solutions

  1. Validate file integrity and re-index / re-export the column data.
  2. Confirm the byte stream is aligned — re-check where stats deserialization starts.
  3. Match reader and writer columnar format versions.
  4. Pre-check the gcd bytes in a custom reader and fail early with context.

Example fix

// before
let stats = ColumnStats::deserialize(&mut reader)?; // InvalidData on gcd==0
// after
if reader peeked gcd is 0 { /* treat as corrupt segment */ }
let stats = ColumnStats::deserialize(&mut reader)?;
Defensive patterns

Strategy: try-catch

Try / catch

match ColumnStats::deserialize(&mut reader) {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData
        && e.to_string().contains("GCD of 0") => {
        // segment stats corrupted: rebuild or use fallback stats
    }
    other => other?,
}

Prevention

When it happens

Trigger: Deserializing ColumnStats where the VInt read for gcd is 0 — e.g. from truncated/corrupted streams, zero-filled buffers, or data written by an incompatible encoder version.

Common situations: Reading column stats from damaged segment files; byte-offset misalignment causing a 0 to be read in the gcd slot; cross-version format mismatch.

Understand the failure class

Related errors


AI-assisted analysis of quickwit-oss/tantivy@b5d8deb80c (2026-09-05). Data as JSON: /api/errors/f86fb7db64b9ca68. Report an issue: GitHub.