risingwavelabs/risingwave · error

UpdateDelete epoch different UpdateInsert epoch

Error message

UpdateDelete epoch {} different UpdateInsert epoch {}

What it means

Guard in the KV log store update decoder: the UpdateInsert half of an update pair belongs to a different epoch than its UpdateDelete half. Both rows of an update must share the same epoch; a mismatch means the stored data is corrupt or the read range straddles updates incorrectly, so decoding fails.

Solutions

  1. An UpdateDelete and its paired UpdateInsert were decoded with different epochs, meaning the update pair is split across epoch boundaries or the log store data is misaligned.
  2. Verify barrier placement so an update pair is never written across epochs; rebuild affected ranges from snapshot if corrupted.
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at src/stream/src/common/log_store_impl/kv_log_store/serde.rs:812 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/09137ee978623dfb. Report an issue: GitHub.

Appendix: source

Thrown at src/stream/src/common/log_store_impl/kv_log_store/serde.rs:812

        while let Some(RawLogStoreRow { row_meta, op }) = stream.try_next().await? {
            if let LogStoreRowOp::Row {
                seq_id,
                op: Op::UpdateDelete,
                row: old_value,
            } = op
            {
                let next_item = stream.try_next().await?;
                if let Some(row) = next_item {
                    let next_op = row.op;
                    let next_row_meta = row.row_meta;
                    if let LogStoreRowOp::Row {
                        seq_id: next_seq_id,
                        op: Op::UpdateInsert,
                        row: new_value,
                    } = next_op
                    {
                        if row_meta.epoch != next_row_meta.epoch {
                            return Err(anyhow!(
                                "UpdateDelete epoch {} different UpdateInsert epoch {}",
                                row_meta.epoch,
                                next_row_meta.epoch
                            ));
                        }
                        let merged_row_meta = RowMeta {
                            vnode: row_meta.vnode,
                            epoch: row_meta.epoch,
                            size: (row_meta.size + next_row_meta.size),
                        };
                        yield LogStoreRow {
                            op: LogStoreOp::Update {
                                seq_id: next_seq_id,
                                new_value,
                                old_value,
                            },
                            row_meta: merged_row_meta,
                        };

View on GitHub (pinned to 6469eb736d)