{"record":{"id":"eaa1fef370e0b9a2","repo":"risingwavelabs/risingwave","slug":"chunk-size-can-t-be-zero","errorCode":null,"errorMessage":"Chunk size can't be zero!","messagePattern":"Chunk size can't be zero!","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/batch/executors/src/executor/join/chunked_data.rs","lineNumber":101,"sourceCode":"                chunk_id: self.chunk_id,\n                row_id: self.row_id + 1,\n            }\n        }\n    }\n}\n\nimpl<V> ChunkedData<V> {\n    pub(super) fn with_chunk_sizes<C>(chunk_sizes: C) -> Result<Self>\n    where\n        C: IntoIterator<Item = usize>,\n        V: Default,\n    {\n        let chunk_sizes = chunk_sizes.into_iter();\n        let mut chunk_offsets = Vec::with_capacity(chunk_sizes.size_hint().0 + 1);\n        let mut cur = 0usize;\n        chunk_offsets.push(0);\n        for chunk_size in chunk_sizes {\n            ensure!(chunk_size > 0, \"Chunk size can't be zero!\");\n            cur += chunk_size;\n            chunk_offsets.push(cur);\n        }\n\n        let mut data = Vec::with_capacity(cur);\n        data.resize_with(cur, V::default);\n\n        Ok(Self {\n            data,\n            chunk_offsets,\n        })\n    }\n\n    fn index_in_data(&self, index: RowId) -> usize {\n        self.chunk_offsets[index.chunk_id()] + index.row_id()\n    }\n\n    pub(super) fn all_row_ids(&self) -> impl Iterator<Item = RowId> + '_ {","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/batch/executors/src/executor/join/chunked_data.rs#L83-L119","documentation":"While merging a decoded `UpdateDelete` row with its paired `UpdateInsert` row, the two halves must share the same epoch. The decoder found `row_meta.epoch` of the UpdateDelete differs from the epoch of the following UpdateInsert, meaning the update pair was split across epochs — an invariant the merge logic cannot safely handle, so it errors out.","triggerScenarios":"Reading/decoding a buffered update pair where the stored `LogStoreOp::Row { op: Op::UpdateDelete }` and the subsequent `LogStoreOp::Row { op: Op::UpdateInsert }` have differing `row_meta.epoch`. Thrown at serde.rs:812.","commonSituations":"A barrier flushed between the delete and insert halves of an update; a writer bug that lets epoch change mid-pair; store corruption or partial write dropping one half and substituting a row from another epoch.","solutions":["Ensure the write side never splits an update pair across epoch boundaries/barriers — flush pairs atomically.","Check serialization ordering so UpdateDelete and UpdateInsert rows are stamped with the same barrier epoch.","Inspect the seq ids around the pair in the KV store to detect truncation or partial writes.","If it appears only after failover, verify replay does not re-write one half of a pair with a new epoch."],"exampleFix":"// before\n// writer: buffer delete and insert halves independently, may flush across barriers\n// after\n// buffer both halves of an update together and flush them under one epoch\nbuffer.push((delete_half, insert_half)); // single epoch, single flush\n","handlingStrategy":"validation","validationCode":"// rust\n// on the write side, verify the pair before buffering to the log store\nanyhow::ensure!(delete_meta.epoch == insert_meta.epoch,\n    \"update pair spans epochs {} != {}\", delete_meta.epoch, insert_meta.epoch);","typeGuard":null,"tryCatchPattern":"// rust\nmatch reader.next_op().await {\n    Ok(op) => handle(op),\n    Err(e) if e.to_string().contains(\"UpdateDelete epoch\") => {\n        tracing::error!(\"update pair split across epochs in log store: {e:#}\");\n        // fatal: investigate writer flush boundaries / corruption\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Flush update delete/insert pairs atomically under one barrier epoch.","Add a write-side invariant that a pair never crosses a flush boundary.","After failover, verify replay does not re-stamp one half of a pair with a new epoch."],"tags":["log-store","deserialization","update-delete-insert","epoch"],"backgroundTag":"invalid-state-transition","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}