{"record":{"id":"805ec4db875af8b3","repo":"risingwavelabs/risingwave","slug":"committed-epoch-mismatch-table-table-id-commit","errorCode":null,"errorMessage":"Committed epoch mismatch: table {table_id}, committed_epoch {committed_epoch}, read_epoch {read_epoch}","messagePattern":"Committed epoch mismatch: table (.+?), committed_epoch (.+?), read_epoch (.+?)","errorType":"exception","errorClass":"HummockError","httpStatus":null,"severity":"error","filePath":"src/storage/src/hummock/error.rs","lineNumber":55,"sourceCode":"    #[error(\"ObjectStore failed with IO error: {0}\")]\n    ObjectIoError(\n        #[from]\n        #[backtrace]\n        ObjectError,\n    ),\n    #[error(\"Meta error: {0}\")]\n    MetaError(String),\n    #[error(\"SharedBuffer error: {0}\")]\n    SharedBufferError(String),\n    #[error(\"Wait epoch error: {0}\")]\n    WaitEpoch(String),\n    #[error(\"Next epoch error: {0}\")]\n    NextEpoch(String),\n    #[error(\"Change log retention miss: table {table_id}, epoch {epoch}\")]\n    ChangeLogRetentionMiss { table_id: TableId, epoch: u64 },\n    #[error(\"Time-travel version expired: table {table_id}, epoch {epoch}\")]\n    TimeTravelVersionExpired { table_id: TableId, epoch: u64 },\n    #[error(\n        \"Committed epoch mismatch: table {table_id}, committed_epoch {committed_epoch}, read_epoch {read_epoch}\"\n    )]\n    CommittedEpochMismatch {\n        table_id: TableId,\n        committed_epoch: u64,\n        read_epoch: u64,\n    },\n    #[error(\"Barrier read is unavailable for now. Likely the cluster is recovering\")]\n    ReadCurrentEpoch,\n    #[error(\"CompactionExecutor error: {0}\")]\n    CompactionExecutor(String),\n    #[error(\"FileCache error: {0}\")]\n    FileCache(String),\n    #[error(\"SstObjectIdTracker error: {0}\")]\n    SstObjectIdTrackerError(String),\n    #[error(\"CompactionGroup error: {0}\")]\n    CompactionGroupError(String),\n    #[error(\"SstableUpload error: {0}\")]","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/storage/src/hummock/error.rs#L37-L73","documentation":"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.","triggerScenarios":"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`.","commonSituations":"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.","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."],"exampleFix":"// before: propagating mismatch as fatal\nlet rows = read_at(read_epoch)?;\n// after: re-pin to the current committed epoch and retry\nlet rows = match read_at(read_epoch) {\n    Err(e) if matches!(e.inner(), HummockErrorInner::CommittedEpochMismatch { .. }) => {\n        read_at(hummock.get_pinned_version().committed_epoch()?)?\n    }\n    other => other?,\n};","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match err.inner() {\n    HummockErrorInner::CommittedEpochMismatch { table_id, committed_epoch, read_epoch } => {\n        // stale epoch pin: re-pin to the current committed epoch and retry once\n    }\n    _ => return Err(err),\n}","preventionTips":["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."],"tags":["storage","hummock","epoch","consistency","race-condition"],"backgroundTag":"invalid-state-transition","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}