risingwavelabs/risingwave · error

truncate at {:?} but latest offset is {:?}

Error message

truncate at {:?} but latest offset is {:?}

What it means

Guard in the in-memory log store's truncate: the requested truncate offset exceeds the latest written offset in the buffer. One cannot truncate data that has not been written yet, so out-of-order or over-advancing truncation is rejected with this error.

Source

Thrown at src/stream/src/common/log_store_impl/in_mem.rs:244

                None => Err(anyhow!("end of log stream")),
            },
            AwaitingTruncate { .. } => std::future::pending().await,
        }
    }

    fn truncate(&mut self, offset: TruncateOffset) -> LogStoreResult<()> {
        // check the truncate offset is higher than prev truncate offset
        if self.truncate_offset >= offset {
            return Err(anyhow!(
                "truncate offset {:?} but prev truncate offset is {:?}",
                offset,
                self.truncate_offset
            ));
        }

        // check the truncate offset does not exceed the latest possible offset
        if offset > self.latest_offset {
            return Err(anyhow!(
                "truncate at {:?} but latest offset is {:?}",
                offset,
                self.latest_offset
            ));
        }

        if let AwaitingTruncate {
            sealed_epoch,
            next_epoch,
        } = &self.epoch_progress
            && let TruncateOffset::Barrier { epoch } = offset
            && epoch == *sealed_epoch
        {
            let sealed_epoch = *sealed_epoch;
            self.epoch_progress = Consuming(*next_epoch);
            self.truncated_epoch_tx
                .send(sealed_epoch)
                .map_err(|_| anyhow!("unable to send sealed epoch"))?;

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Clamp the truncate offset to latest_offset before calling truncate
  2. Verify where the offset came from (checkpoint, watermark) for corruption or version skew
  3. Fix offset computation logic that predicts beyond written entries

Example fix

// before
log_store.truncate(watermark).await?;
// after
log_store.truncate(watermark.min(latest_offset)).await?;
Defensive patterns

Strategy: validation

Validate before calling

fn sanitize_truncate_offset(offset: u64, latest_offset: u64) -> u64 {
    offset.min(latest_offset)
}

Try / catch

let safe = offset.min(latest_offset);
if safe > current_truncate_offset {
    log_store.truncate(safe).await?;
}

Prevention

When it happens

Trigger: truncate(offset) invoked when offset > self.latest_offset, e.g. a consumer computing a future offset or using a stale/garbage offset value.

Common situations: Bugs in offset bookkeeping (u64 underflow/overflow), mismatched offset domains between log store versions, or corrupted checkpoint metadata supplying bad offsets.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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