risingwavelabs/risingwave · error

Iceberg metadata scan should not have input executors

Error message

Iceberg metadata scan should not have input executors

What it means

Same epoch-consistency check as error 2022 but for the update path: when decoding a `LogStoreOp::Update` (UpdateDelete/UpdateInsert pair), the row's `row_meta.epoch` must equal the read's `expected_epoch`. A mismatch means the stored update was tagged with a different epoch than the range being read, and the decoder refuses to emit the pair.

Source

Thrown at src/batch/executors/src/executor/iceberg_metadata_scan.rs:77

            table,
            self.metadata_type,
            self.time_travel_info,
            self.chunk_size,
        ) {
            yield chunk?;
        }
    }
}

pub struct IcebergMetadataScanExecutorBuilder;

impl BoxedExecutorBuilder for IcebergMetadataScanExecutorBuilder {
    async fn new_boxed_executor(
        source: &ExecutorBuilder<'_>,
        inputs: Vec<BoxedExecutor>,
    ) -> crate::error::Result<BoxedExecutor> {
        ensure!(
            inputs.is_empty(),
            "Iceberg metadata scan should not have input executors"
        );
        let node = try_match_expand!(
            source.plan_node().get_node_body().unwrap(),
            NodeBody::IcebergMetadataScan
        )?;

        let metadata_type = match MetadataType::try_from(node.metadata_type)
            .context("invalid Iceberg metadata type")?
        {
            MetadataType::Snapshots => IcebergMetadataTableType::Snapshots,
            MetadataType::Manifests => IcebergMetadataTableType::Manifests,
            MetadataType::Files => IcebergMetadataTableType::Files,
            MetadataType::Unspecified => {
                return Err(anyhow!("Iceberg metadata type is unspecified").into());
            }
        };
        let time_travel_info = node

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Check the serialize path writes `row_meta.epoch` from the same barrier epoch used to compute expected_epoch on read.
  2. Confirm update rows are never split across epoch boundaries when buffered/flushed.
  3. Inspect the offending seq id's row_meta to detect corruption or miswritten epochs.
  4. Ensure the read range was selected for the epoch that actually contains the rows.
Defensive patterns

Strategy: validation

Validate before calling

// rust
// ensure updates are flushed within a single epoch
anyhow::ensure!(update_epoch == barrier_epoch, "update tagged epoch {} but flush epoch {}", update_epoch, barrier_epoch);

Try / catch

// rust
match reader.deserialize_stream_chunk(start, end, expected_epoch).await {
    Ok(chunk) => process(chunk),
    Err(e) if e.to_string().contains("decoded epoch") => {
        tracing::error!("update epoch mismatch, possible corruption: {e:#}");
        // route to recovery instead of retrying the same read
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `deserialize_stream_chunk` on a range where a stored `LogStoreOp::Update` row has `row_meta.epoch != expected_epoch`. Thrown at serde.rs:489.

Common situations: Writer tagging update rows with a stale epoch across a barrier; reads whose expected epoch was computed from a different barrier; store corruption from an unclean shutdown mid-write.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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