risingwavelabs/risingwave · error

no table changelog found for upstream table {} when resolvin

Error message

no table changelog found for upstream table {} when resolving since_timestamp

What it means

When resolving a since_timestamp into a concrete log-store epoch for a snapshot backfill, the upstream table's change log entry is missing entirely from TableChangeLogs. The resolver needs at least one changelog entry to map the since epoch to a snapshot checkpoint epoch.

Source

Thrown at src/meta/src/barrier/context/context_impl.rs:64

    BarrierManagerStatus, BarrierWorkerRuntimeInfoSnapshot, BatchRefreshInfo, Command,
    CreateStreamingJobCommandInfo, CreateStreamingJobType, DatabaseRuntimeInfoSnapshot,
    RecoveryReason, ReplaceStreamJobPlan, Scheduled,
};
use crate::hummock::CommitEpochInfo;
use crate::manager::LocalNotification;
use crate::model::FragmentDownstreamRelation;
use crate::serving::{fetch_serving_infos, sync_serving_table_vnode_mappings_to_hummock};
use crate::stream::{SourceChange, cleanup_dropped_streaming_jobs};
use crate::{MetaError, MetaResult};

fn resolve_since_timestamp_log_store_epoch(
    table_id: TableId,
    since_epoch: u64,
    upstream_committed_epoch: u64,
    table_change_log: &TableChangeLogs,
) -> MetaResult<SinceTimestampResolvedEpoch> {
    let change_log = table_change_log.get(&table_id).ok_or_else(|| {
        anyhow::anyhow!(
            "no table changelog found for upstream table {} when resolving since_timestamp",
            table_id
        )
    })?;
    let Some(first_log) = change_log.first() else {
        return Err(anyhow::anyhow!(
            "empty table changelog found for upstream table {} when resolving since_timestamp",
            table_id
        )
        .into());
    };
    let first_checkpoint_epoch = first_log.checkpoint_epoch;
    if since_epoch < first_checkpoint_epoch {
        return Err(anyhow::anyhow!(
            "since_timestamp is earlier than the retained changelog of upstream table {}: requested epoch {}, first retained checkpoint epoch {}",
            table_id,
            since_epoch,
            first_checkpoint_epoch,

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Ensure the upstream table's changelog is registered before creating the snapshot backfill
  2. Retry after the next barrier collects change logs
  3. Verify the upstream table id is correct
  4. Report as a bug if the table clearly has changelogs
Defensive patterns

Strategy: validation

Validate before calling

if table_change_log.get(&table_id).is_none() {
    return Err("upstream changelog not registered yet");
}

Type guard

fn has_changelog(logs: &TableChangeLogs, id: &TableId) -> bool {
    logs.contains_key(id)
}

Try / catch

match create_snapshot_backfill(...).await {
    Err(e) if e.contains("no table changelog found") => retry_after_next_barrier(),
    other => other,
}

Prevention

When it happens

Trigger: Calling resolve_since_timestamp_log_store_epoch (via resolve_log_store_epoch during barrier creation for a snapshot backfill) with a table_id absent from table_change_log.

Common situations: Upstream table changelog not yet registered in the barrier state; upstream MV/table created but no changelog collected yet; internal state inconsistency after recovery.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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