risingwavelabs/risingwave · error · anyhow::Error

log_store_rewind_start_epoch {} not later than first_epoch {

Error message

log_store_rewind_start_epoch {} not later than first_epoch {}

What it means

After rewinding the sink's log store reader to log_store_rewind_start_epoch, the coordinator expects the first log item's epoch to be strictly later than the rewind start epoch. If the first item's epoch is >= the rewind start, the rewind semantics are violated and the log would be consumed from a point at or before the requested epoch, so it bails.

Source

Thrown at src/connector/src/sink/coordinate.rs:87

        })
    }
}

#[async_trait]
impl<W: SinkWriter<CommitMetadata = Option<SinkMetadata>>> LogSinker for CoordinatedLogSinker<W> {
    async fn consume_log_and_sink(self, mut log_reader: impl SinkLogReader) -> Result<!> {
        let (mut coordinator_stream_handle, log_store_rewind_start_epoch) = self
            .sink_coordinate_client
            .new_stream_handle(&self.param, self.vnode_bitmap)
            .await?;
        let mut sink_writer = self.writer;
        log_reader.start_from(log_store_rewind_start_epoch).await?;
        let mut first_item = log_reader.next_item().await?;
        if let (Some(log_store_rewind_start_epoch), (first_epoch, _)) =
            (log_store_rewind_start_epoch, &first_item)
        {
            if log_store_rewind_start_epoch >= *first_epoch {
                bail!(
                    "log_store_rewind_start_epoch {} not later than first_epoch {}",
                    log_store_rewind_start_epoch,
                    first_epoch
                );
            }
        } else {
            let &(initial_epoch, _) = &first_item;
            let aligned_initial_epoch = coordinator_stream_handle
                .align_initial_epoch(initial_epoch)
                .await?;
            if initial_epoch != aligned_initial_epoch {
                warn!(
                    initial_epoch,
                    aligned_initial_epoch,
                    sink_id = %self.param.sink_id,
                    "initial epoch not matched aligned initial epoch"
                );
                let mut peeked_first = Some(first_item);

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Check the log store to confirm the rewind start epoch is strictly less than the first retained epoch.
  2. Retrigger recovery with a corrected rewind start epoch (usually the last checkpoint epoch).
  3. Inspect meta log store cleanup/retention to ensure it did not trim past the requested epoch.
Defensive patterns

Strategy: retry

Validate before calling

if let Some(start) = log_store_rewind_start_epoch {
    if start >= first_epoch_in_log {
        // adjust rewind point to last checkpoint epoch before retrying
    }
}

Try / catch

match result {
    Err(e) if e.to_string().contains("log_store_rewind_start_epoch") => retry_with_last_checkpoint_epoch(),
    other => other,
}

Prevention

When it happens

Trigger: consume_log_and_sink is called with a rewind start epoch, and the log reader's first returned item carries first_epoch such that log_store_rewind_start_epoch >= first_epoch (rewind point not strictly before the first available item).

Common situations: Recovery after failover where the requested rewind epoch equals the first epoch still present in the log store; stale log store state after migration or incorrect epoch bookkeeping in the meta log store.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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