risingwavelabs/risingwave · error

new item epoch {} does not match current chunk offset epoch

Error message

new item epoch {} does not match current chunk offset epoch {}

What it means

`check_next_item_epoch` on a `TruncateOffset::Chunk` requires that any new item written to the log store carries exactly the same epoch as the current chunk offset epoch. A mismatch means an item from a different epoch is being appended into a chunk opened for another epoch.

Source

Thrown at src/connector/src/sink/log_store.rs:96

        if *self >= next_offset {
            bail!(
                "next offset {:?} should be later than current offset {:?}",
                next_offset,
                self
            )
        } else {
            Ok(())
        }
    }

    pub fn check_next_item_epoch(&self, epoch: u64) -> LogStoreResult<()> {
        match self {
            TruncateOffset::Chunk {
                epoch: offset_epoch,
                ..
            } => {
                if epoch != *offset_epoch {
                    bail!(
                        "new item epoch {} does not match current chunk offset epoch {}",
                        epoch,
                        offset_epoch
                    );
                }
            }
            TruncateOffset::Barrier {
                epoch: offset_epoch,
            } => {
                if epoch <= *offset_epoch {
                    bail!(
                        "new item epoch {} does not exceed barrier offset epoch {}",
                        epoch,
                        offset_epoch
                    );
                }
            }
        }

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Close/rotate the current chunk and advance the offset when the item's epoch differs from the chunk epoch
  2. Verify the item's epoch is stamped from the same source as the chunk epoch
  3. Check barrier handling so epoch transitions always precede new items

Example fix

// before
store.check_next_item_epoch(new_epoch); // chunk opened with old_epoch
// after
if new_epoch != chunk_epoch {
    store.flush_chunk_and_advance(new_epoch);
}
store.check_next_item_epoch(new_epoch);
Defensive patterns

Strategy: validation

Validate before calling

assert_eq!(item_epoch, chunk_epoch, "item epoch must match open chunk epoch");

Type guard

fn matches_chunk_epoch(epoch: u64, offset: &TruncateOffset) -> bool {
    matches!(offset, TruncateOffset::Chunk { epoch: e, .. } if *e == epoch)
}

Try / catch

match offset.check_next_item_epoch(epoch) {
    Err(e) => { store.rotate_chunk(epoch)?; store.check_next_item_epoch(epoch)?; }
    Ok(()) => {}
}

Prevention

When it happens

Trigger: Calling `check_next_item_epoch(epoch)` while the current offset is `TruncateOffset::Chunk { epoch: offset_epoch, .. }` and `epoch != offset_epoch`; typically when a sink item's epoch changed mid-chunk without closing the chunk first.

Common situations: Sink writers mixing items from adjacent epochs; barrier handling that fails to flush/rotate the chunk before the epoch advances.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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