risingwavelabs/risingwave · error · anyhow::Error

truncate offset {:?} but prev truncate offset is {:?}

Error message

truncate offset {:?} but prev truncate offset is {:?}

What it means

Guard in the in-memory log store's truncate: the requested truncate offset is not strictly newer than the previously applied truncate offset (it is <= it). Truncation must monotonically advance; a stale or duplicated truncate request indicates a bug in the caller and returns this error.

Source

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

                            LogStoreReadItem::Barrier {
                                is_checkpoint: options.is_checkpoint,
                                new_vnode_bitmap: options.new_vnode_bitmap,
                                is_stop: options.is_stop,
                                schema_change: options.schema_change,
                            },
                        ))
                    }
                },
                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,

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Ensure the truncation offset is derived from the minimum consumed offset and is monotonic
  2. Guard callers to skip truncate when offset <= current truncate offset
  3. Inspect epoch/consumption tracking for double-truncate races

Example fix

// before
log_store.truncate(offset).await?;
// after
if offset > current_truncate_offset {
    log_store.truncate(offset).await?;
}
Defensive patterns

Strategy: validation

Validate before calling

fn should_truncate(offset: u64, prev_truncate_offset: u64) -> bool {
    offset > prev_truncate_offset
}

Try / catch

if offset > current_truncate_offset {
    log_store.truncate(offset).await?;
}

Prevention

When it happens

Trigger: truncate(offset) invoked when self.truncate_offset >= offset, i.e. a duplicate or out-of-order truncate request.

Common situations: Bugs in min-working-epoch tracking that compute a stale truncate offset, multiple components racing to truncate, or recovery replaying an old truncation.

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/d241072b230a472f. Report an issue: GitHub.