risingwavelabs/risingwave · error
current epoch {} has exceeded the epoch {} of the stream tha
Error message
current epoch {} has exceeded the epoch {} of the stream that has not started What it means
Thrown while draining `not_started_streams` during barrier validation: the incoming epoch has skipped past the epoch recorded for a stream that had not yet started, so its bookkeeping entry can never be matched. The log store tracks streams whose first epoch is in the future; if a barrier arrives with an epoch greater than such a stream's first epoch, the stream's state is inconsistent and cannot be reconciled.
Source
Thrown at src/stream/src/common/log_store_impl/kv_log_store/serde.rs:972
}
};
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
));
}
let (_, stream) = self.not_started_streams.pop().expect("should not be empty");
self.row_streams.push(stream.into_future());
}
self.stream_state = StreamState::AllConsumingRow { curr_epoch: epoch };
Ok(())
}
async fn next_op(&mut self) -> LogStoreResult<Option<AlignedLogStoreRow>> {
while let (Some(result), stream) = self
.row_streams
.next()
.awaitView on GitHub (pinned to 6469eb736d)
Solutions
- Verify that a stream's registered first epoch matches the epoch of the first barrier it actually receives.
- Check barrier flow so streams that have not started are not skipped by later epochs; resume/start them before the epoch advances past their registration.
- Confirm meta node stream-building completion ordering: a stream must start before barriers with greater epochs are validated.
- Log `not_started_streams` contents when the error fires to identify which stream's epoch was skipped.
Example fix
// before: barrier epoch advanced past not-started stream stream_epoch = 100; barrier_epoch = 200; // 100 < 200 -> error // after: ensure stream starts at/before barrier epoch stream_epoch = 200; barrier_epoch = 200; // equal -> popped and matched
Defensive patterns
Strategy: validation
Validate before calling
// Rust: before validating a barrier, ensure no not-started stream would be skipped
fn would_skip_epoch(not_started_streams: &[u64], epoch: u64) -> bool {
not_started_streams.iter().any(|s| s < &epoch)
} Prevention
- Register streams with the exact epoch of their first barrier.
- Ensure stream building/creation completes before barriers of later epochs are processed.
- Monitor creation-to-first-barrier latency to catch streams stuck in not-started state.
When it happens
Trigger: In `validate_epoch`, when popping `not_started_streams` and finding `*stream_epoch < epoch` — a barrier with epoch E arrives for a stream whose recorded first epoch is smaller than E (epoch skipped over it).
Common situations: A stream was registered with an expected start epoch but its data/barriers were processed under a later epoch (actor creation delayed, backfill altered epochs), or meta node updated the stream epoch list inconsistently with barrier flow.
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
- epoch {} does not match with current epoch {}
- epoch {} should be greater than prev epoch {}
- Exchange executor should not have children!
- missing lagging barriers for direct log-store start from sna
- get different response epoch to commit epoch: {} {}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/52c962b5b3a1100e.
Report an issue: GitHub.