risingwavelabs/risingwave · error

Iceberg metadata type is unspecified

Error message

Iceberg metadata type is unspecified

What it means

Size-bound violation on the update path: decoding an update contributes two ops (`Op::UpdateDelete` and `Op::UpdateInsert`), and the running `ops.len()` exceeded `size_bound` after pushing both. This guards against oversized chunks consuming unbounded memory during decode.

Source

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

        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
            .time_travel
            .as_ref()
            .map(|time_travel| match time_travel {
                TimeTravel::SnapshotId(snapshot_id) => IcebergTimeTravelInfo::Version(*snapshot_id),
                TimeTravel::TimestampMs(timestamp_ms) => {
                    IcebergTimeTravelInfo::TimestampMs(*timestamp_ms)
                }
            });
        let config = ConnectorProperties::extract(
            WithOptionsSecResolved::new(node.with_properties.clone(), node.secret_refs.clone()),
            false,
        )?;
        let ConnectorProperties::Iceberg(properties) = config else {
            return Err(anyhow!("Iceberg metadata scan received a non-Iceberg connector").into());
        };

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Make the write side count updates as two ops against size_bound when serializing chunks.
  2. Tighten the read range selection so it stops before exceeding size_bound ops.
  3. Increase size_bound consistently on both writer and reader if larger chunks are intended.
  4. Check for duplicated update entries in the KV store causing the count to inflate.

Example fix

// before
// writer counts each update as 1 op toward size_bound
if chunk_ops.len() > size_bound { flush(); }
// after
if chunk_ops.len() + 2 > size_bound { flush(); } // update = delete + insert
Defensive patterns

Strategy: validation

Validate before calling

// rust
// count an update pair as 2 ops when checking size_bound before write
anyhow::ensure!(pending_ops + 2 <= size_bound, "update pair would exceed size_bound {}", size_bound);

Prevention

When it happens

Trigger: Calling `deserialize_stream_chunk` where decoded update pairs push total op count past `size_bound` (checked after `read_update(row_size)` and the two `ops.push` calls). Thrown at serde.rs:499.

Common situations: Write side packing many update pairs into one chunk while the reader uses a size_bound counted in single rows (each update counts as two); read ranges covering more than one chunk's worth of updates.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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