risingwavelabs/risingwave · error · HummockError
Committed epoch mismatch: table {table_id}, committed_epoch
Error message
Committed epoch mismatch: table {table_id}, committed_epoch {committed_epoch}, read_epoch {read_epoch} What it means
Hummock reports that the epoch a caller tried to read does not match the epoch that was actually committed for the table. Reads are pinned to a specific committed epoch; if the expected `committed_epoch` differs from the `read_epoch` being served, returning data would violate the versioned-snapshot guarantee, so the engine refuses. Created via `HummockError::committed_epoch_mismatch(table_id, committed_epoch, read_epoch)` in src/storage/src/hummock/error.rs:132.
Source
Thrown at src/storage/src/hummock/error.rs:55
#[error("ObjectStore failed with IO error: {0}")]
ObjectIoError(
#[from]
#[backtrace]
ObjectError,
),
#[error("Meta error: {0}")]
MetaError(String),
#[error("SharedBuffer error: {0}")]
SharedBufferError(String),
#[error("Wait epoch error: {0}")]
WaitEpoch(String),
#[error("Next epoch error: {0}")]
NextEpoch(String),
#[error("Change log retention miss: table {table_id}, epoch {epoch}")]
ChangeLogRetentionMiss { table_id: TableId, epoch: u64 },
#[error("Time-travel version expired: table {table_id}, epoch {epoch}")]
TimeTravelVersionExpired { table_id: TableId, epoch: u64 },
#[error(
"Committed epoch mismatch: table {table_id}, committed_epoch {committed_epoch}, read_epoch {read_epoch}"
)]
CommittedEpochMismatch {
table_id: TableId,
committed_epoch: u64,
read_epoch: u64,
},
#[error("Barrier read is unavailable for now. Likely the cluster is recovering")]
ReadCurrentEpoch,
#[error("CompactionExecutor error: {0}")]
CompactionExecutor(String),
#[error("FileCache error: {0}")]
FileCache(String),
#[error("SstObjectIdTracker error: {0}")]
SstObjectIdTrackerError(String),
#[error("CompactionGroup error: {0}")]
CompactionGroupError(String),
#[error("SstableUpload error: {0}")]View on GitHub (pinned to 6469eb736d)
Solutions
- Re-fetch a fresh current/read epoch from the Hummock version (e.g. via `ReadCurrentEpoch` semantics) and retry the read at the new epoch.
- Treat the read as aborted and re-execute the query/snapshot read; the epoch pin was stale, not the data.
- If it happens persistently for one table, check for barrier/DDL churn or failover issues around that table's commits.
- Enable tracing on `committed_epoch_mismatch` call sites to correlate with barriers and identify the race window.
Example fix
// before: propagating mismatch as fatal
let rows = read_at(read_epoch)?;
// after: re-pin to the current committed epoch and retry
let rows = match read_at(read_epoch) {
Err(e) if matches!(e.inner(), HummockErrorInner::CommittedEpochMismatch { .. }) => {
read_at(hummock.get_pinned_version().committed_epoch()?)?
}
other => other?,
}; Defensive patterns
Strategy: retry
Try / catch
match err.inner() {
HummockErrorInner::CommittedEpochMismatch { table_id, committed_epoch, read_epoch } => {
// stale epoch pin: re-pin to the current committed epoch and retry once
}
_ => return Err(err),
} Prevention
- Always re-fetch the current committed epoch before each read instead of caching it across barriers.
- Treat epoch-pinned reads as one-shot: never reuse a pinned epoch after a barrier or failover event.
- Serialize reads that race with DDL/barrier churn on the same table.
- Add a bounded retry (once or twice) around epoch-pinned reads.
When it happens
Trigger: A read or streaming read pinned to one epoch encounters a Hummock version whose committed epoch changed underneath it — e.g. a barrier advances/commits a new epoch between when the read epoch was chosen and when the data is fetched for `table_id`.
Common situations: Concurrent DDL/barriers racing with a batch or stream read; failover or cluster recovery rebuilding epoch state; a stale frontend/compute node reading with an epoch from before a recovery; retrying a request against a newly promoted leader with fresh epoch history.
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
- job {} in database {} has tables with different table ids. {
- Wait epoch error: {0}
- Next epoch error: {0}
- Change log retention miss: table {table_id}, epoch {epoch}
- Time-travel version expired: table {table_id}, epoch {epoch}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/805ec4db875af8b3.
Report an issue: GitHub.