risingwavelabs/risingwave · error · SinkError::Iceberg

Can't create iceberg sink write result from empty data!

Error message

Can't create iceberg sink write result from empty data!

What it means

When committing an Iceberg sink, `IcebergCommitResult::try_from` deserializes the stored SinkMetadata. If `value.metadata` is absent (not `Serialized(_)`), there are no serialized data files to build a commit result from, so it bails with this error.

Source

Thrown at src/connector/src/sink/iceberg/commit.rs:64

use crate::connector_common::{IcebergCommittedSnapshot, IcebergSinkCompactionUpdate};
use crate::sink::catalog::SinkId;
use crate::sink::{Result, SinglePhaseCommitCoordinator, SinkParam, TwoPhaseCommitCoordinator};

const SCHEMA_ID: &str = "schema_id";
const PARTITION_SPEC_ID: &str = "partition_spec_id";
const DATA_FILES: &str = "data_files";

#[derive(Default, Clone)]
pub struct IcebergCommitResult {
    pub schema_id: i32,
    pub partition_spec_id: i32,
    pub data_files: Vec<SerializedDataFile>,
}

impl IcebergCommitResult {
    pub fn try_from(value: &SinkMetadata) -> Result<Self> {
        let Some(Serialized(value)) = &value.metadata else {
            bail!("Can't create iceberg sink write result from empty data!");
        };

        Self::try_from_serialized_bytes(&value.metadata)
    }

    pub fn try_from_serialized_bytes(value: &[u8]) -> Result<Self> {
        let mut values = if let serde_json::Value::Object(value) =
            serde_json::from_slice::<serde_json::Value>(value)
                .context("Can't parse iceberg sink metadata")?
        {
            value
        } else {
            bail!("iceberg sink metadata should be an object");
        };

        let schema_id;
        if let Some(serde_json::Value::Number(value)) = values.remove(SCHEMA_ID) {
            schema_id = value

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Check whether the sink ever committed metadata; if the epoch legitimately wrote no files, ensure the empty-metadata case is handled upstream before calling try_from
  2. Inspect the meta store / sink metadata records for the affected epoch
  3. Drop and recreate the sink if its metadata is unrecoverable
  4. Verify connector/node versions are consistent to avoid serialization mismatches
Defensive patterns

Strategy: try-catch

Validate before calling

// guard before calling IcebergCommitResult::try_from
if !matches!(meta.metadata, Some(Serialized(_))) {
    // handle empty-metadata epoch explicitly
    return Ok(IcebergCommitResult::default());
}

Type guard

fn has_serialized_metadata(m: &SinkMetadata) -> bool {
    matches!(m.metadata, Some(Serialized(_)))
}

Try / catch

let res = IcebergCommitResult::try_from(&meta);
if let Err(e) = &res {
    if e.to_string().contains("from empty data") {
        // treat as no-op commit or alert on missing metadata
    }
}

Prevention

When it happens

Trigger: Reading sink metadata (e.g. during MV/commit recovery or `SinkCommittedResult` handling) where the SinkMetadata was never written — e.g. the sink epoch produced no metadata, or the metadata field is empty/None in the persisted record.

Common situations: Corrupted or pruned metadata in the meta store; recovering a sink whose previous epoch never emitted metadata; version/serialization changes causing metadata to be dropped; bug in the metadata-writing path.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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