{"record":{"id":"f86fb7db64b9ca68","repo":"quickwit-oss/tantivy","slug":"invaliddata-f86fb7","errorCode":"InvalidData","errorMessage":"GCD of 0 is forbidden","messagePattern":"GCD of 0 is forbidden","errorType":"error_code","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"columnar/src/column_values/stats.rs","lineNumber":43,"sourceCode":"    pub fn amplitude(&self) -> u64 {\n        self.max_value - self.min_value\n    }\n}\n\nimpl BinarySerializable for ColumnStats {\n    fn serialize<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {\n        VInt(self.min_value).serialize(writer)?;\n        VInt(self.gcd.get()).serialize(writer)?;\n        VInt(self.amplitude() / self.gcd).serialize(writer)?;\n        VInt(self.num_rows as u64).serialize(writer)?;\n        Ok(())\n    }\n\n    fn deserialize<R: io::Read>(reader: &mut R) -> io::Result<Self> {\n        let min_value = VInt::deserialize(reader)?.0;\n        let gcd = VInt::deserialize(reader)?.0;\n        let gcd = NonZeroU64::new(gcd)\n            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, \"GCD of 0 is forbidden\"))?;\n        let amplitude = VInt::deserialize(reader)?.0 * gcd.get();\n        let max_value = min_value + amplitude;\n        let num_rows = VInt::deserialize(reader)?.0 as RowId;\n        Ok(ColumnStats {\n            min_value,\n            max_value,\n            num_rows,\n            gcd,\n        })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::num::NonZeroU64;\n\n    use common::BinarySerializable;\n","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/quickwit-oss/tantivy/blob/b5d8deb80c26924e6b007a5b1a7630f35ca64de4/columnar/src/column_values/stats.rs#L25-L61","documentation":"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.","triggerScenarios":"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.","commonSituations":"Reading column stats from damaged segment files; byte-offset misalignment causing a 0 to be read in the gcd slot; cross-version format mismatch.","solutions":["Validate file integrity and re-index / re-export the column data.","Confirm the byte stream is aligned — re-check where stats deserialization starts.","Match reader and writer columnar format versions.","Pre-check the gcd bytes in a custom reader and fail early with context."],"exampleFix":"// before\nlet stats = ColumnStats::deserialize(&mut reader)?; // InvalidData on gcd==0\n// after\nif reader peeked gcd is 0 { /* treat as corrupt segment */ }\nlet stats = ColumnStats::deserialize(&mut reader)?;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match ColumnStats::deserialize(&mut reader) {\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidData\n        && e.to_string().contains(\"GCD of 0\") => {\n        // segment stats corrupted: rebuild or use fallback stats\n    }\n    other => other?,\n}","preventionTips":["Verify segment integrity before deserializing stats.","Keep reader/writer columnar format versions aligned.","Re-check byte offsets feeding the stats stream to avoid misaligned reads.","Rebuild indexes that were partially written by crashed processes."],"tags":["io","deserialization","gcd","corruption"],"backgroundTag":"gcd-zero-forbidden","analyzedSha":"b5d8deb80c26924e6b007a5b1a7630f35ca64de4","analyzedAt":"2026-09-05T13:20:51.521Z","contentChangedAt":"2026-09-05T13:20:51.521Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}