{"record":{"id":"e6ca0e02e875e40b","repo":"risingwavelabs/risingwave","slug":"empty-table-changelog-found-for-upstream-table","errorCode":null,"errorMessage":"empty table changelog found for upstream table {} when resolving since_timestamp","messagePattern":"empty table changelog found for upstream table (.+?) when resolving since_timestamp","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/meta/src/barrier/context/context_impl.rs","lineNumber":70,"sourceCode":"use crate::model::FragmentDownstreamRelation;\nuse crate::serving::{fetch_serving_infos, sync_serving_table_vnode_mappings_to_hummock};\nuse crate::stream::{SourceChange, cleanup_dropped_streaming_jobs};\nuse crate::{MetaError, MetaResult};\n\nfn resolve_since_timestamp_log_store_epoch(\n    table_id: TableId,\n    since_epoch: u64,\n    upstream_committed_epoch: u64,\n    table_change_log: &TableChangeLogs,\n) -> MetaResult<SinceTimestampResolvedEpoch> {\n    let change_log = table_change_log.get(&table_id).ok_or_else(|| {\n        anyhow::anyhow!(\n            \"no table changelog found for upstream table {} when resolving since_timestamp\",\n            table_id\n        )\n    })?;\n    let Some(first_log) = change_log.first() else {\n        return Err(anyhow::anyhow!(\n            \"empty table changelog found for upstream table {} when resolving since_timestamp\",\n            table_id\n        )\n        .into());\n    };\n    let first_checkpoint_epoch = first_log.checkpoint_epoch;\n    if since_epoch < first_checkpoint_epoch {\n        return Err(anyhow::anyhow!(\n            \"since_timestamp is earlier than the retained changelog of upstream table {}: requested epoch {}, first retained checkpoint epoch {}\",\n            table_id,\n            since_epoch,\n            first_checkpoint_epoch,\n        )\n        .into());\n    }\n    if since_epoch >= upstream_committed_epoch {\n        return Err(anyhow::anyhow!(\n            \"since_timestamp is not before the committed epoch of upstream table {}: requested epoch {}, committed epoch {}\",","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/meta/src/barrier/context/context_impl.rs#L52-L88","documentation":"The changelog map has an entry for the upstream table but it is empty, so there is no first log entry from which to derive the earliest retained checkpoint epoch. The resolver treats this as invalid because nothing has been retained.","triggerScenarios":"Calling resolve_since_timestamp_log_store_epoch with a change_log vec of length zero for the table (e.g. logs trimmed or not yet populated).","commonSituations":"Changelog entry created but logs never appended; aggressive log trimming removed all entries; newly created upstream table with no committed changelogs yet.","solutions":["Wait until the upstream table has committed changelogs, then retry the backfill creation","Retry after the next barrier checkpoints","Check log-store retention configuration so changelogs are not trimmed to empty"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if table_change_log.get(&table_id).map_or(true, |l| l.is_empty()) {\n    return Err(\"upstream has no retained changelogs yet\");\n}","typeGuard":"fn has_nonempty_changelog(logs: &TableChangeLogs, id: &TableId) -> bool {\n    logs.get(id).map_or(false, |l| !l.is_empty())\n}","tryCatchPattern":"match create_snapshot_backfill(...).await {\n    Err(e) if e.contains(\"empty table changelog\") => retry_after_upstream_commits(),\n    other => other,\n}","preventionTips":["Ensure upstream has committed changelogs before since-timestamp backfill","Avoid trimming changelogs to empty","Retry after next checkpoint"],"tags":["meta","barrier","changelog","empty"],"backgroundTag":"empty-required-field","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}