risingwavelabs/risingwave · error

epoch {} should be greater than prev epoch {}

Error message

epoch {} should be greater than prev epoch {}

What it means

Thrown by the same epoch validation routine when the incoming epoch is not strictly greater than the previous epoch recorded in `StreamState::BarrierEmitted { prev_epoch }`. Epochs must monotonically increase across barriers; reusing or decreasing an epoch would corrupt the ordering guarantees of the log store and downstream Hummock snapshots.

Source

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

        let prev_epoch = match &self.stream_state {
            StreamState::Uninitialized => unreachable!("should have init"),
            StreamState::BarrierEmitted { prev_epoch } => *prev_epoch,
            StreamState::AllConsumingRow { curr_epoch }
            | StreamState::BarrierAligning { curr_epoch, .. } => {
                return if *curr_epoch != epoch {
                    Err(anyhow!(
                        "epoch {} does not match with current epoch {}",
                        epoch,
                        curr_epoch
                    ))
                } else {
                    Ok(())
                };
            }
        };

        if prev_epoch >= epoch {
            return Err(anyhow!(
                "epoch {} should be greater than prev epoch {}",
                epoch,
                prev_epoch
            ));
        }

        while let Some((stream_epoch, _)) = self.not_started_streams.last() {
            if *stream_epoch > epoch {
                // Current epoch has not reached the first epoch of
                // the stream. Later streams must also have greater epoch, so break here.
                break;
            }
            if *stream_epoch < epoch {
                return Err(anyhow!(
                    "current epoch {} has exceeded the epoch {} of the stream that has not started",
                    epoch,
                    stream_epoch
                ));

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Ensure every barrier carries a strictly increasing epoch from the epoch provider (meta service); never reuse an epoch after emitting it.
  2. Check for duplicate barrier sends in the barrier manager / actor graph.
  3. During failover, confirm the new leader continues epoch allocation above all previously emitted epochs (e.g. persisted high-water mark).
  4. Add an assertion/log where epochs are generated to catch non-monotonic allocation early.

Example fix

// before
let epoch = last_emitted_epoch; // reused epoch
// after
let epoch = last_emitted_epoch + 1; // strictly greater
Defensive patterns

Strategy: validation

Validate before calling

// Rust: assert strictly increasing epochs before emitting a barrier
fn ensure_monotonic(prev: u64, next: u64) -> Result<(), String> {
    if next <= prev { return Err(format!("epoch {} must be > prev {}", next, prev)); }
    Ok(())
}

Prevention

When it happens

Trigger: Issuing a barrier (via the log store append path) whose `epoch <= prev_epoch`, i.e. after a barrier has already been emitted with an equal or larger epoch.

Common situations: Duplicate barrier injection (same barrier sent twice), a recovery path re-emitting an old epoch, meta node reassigning an epoch after failover without bumping it, or clock/epoch-generation logic producing repeated values.

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