risingwavelabs/risingwave · error · SinkError::Config

'copy-on-write' mode is not supported for append-only iceber

Error message

'copy-on-write' mode is not supported for append-only iceberg sink. Please use 'merge-on-read' instead, which is strictly better for append-only workloads.

What it means

The iceberg sink validates that an append-only sink (sink_type = 'append-only') never uses write_mode = 'copy-on-write'. Because copy-on-write offers no benefit for workloads that never update/delete existing rows, the library hard-rejects this combination and directs users to 'merge-on-read'. The check runs in validate_append_only_write_mode during sink creation.

Source

Thrown at src/connector/src/sink/iceberg/config.rs:531

            IcebergCommon::enforce_one(prop)?;
        }
        Ok(())
    }

    fn enforce_one(prop: &str) -> crate::error::ConnectorResult<()> {
        IcebergCommon::enforce_one(prop)
    }
}

impl IcebergConfig {
    /// Validate that append-only sinks use merge-on-read mode
    /// Copy-on-write is strictly worse than merge-on-read for append-only workloads
    pub fn validate_append_only_write_mode(
        sink_type: &str,
        write_mode: IcebergWriteMode,
    ) -> Result<()> {
        if sink_type == SINK_TYPE_APPEND_ONLY && write_mode == IcebergWriteMode::CopyOnWrite {
            return Err(SinkError::Config(anyhow!(
                "'copy-on-write' mode is not supported for append-only iceberg sink. \
                 Please use 'merge-on-read' instead, which is strictly better for append-only workloads."
            )));
        }
        Ok(())
    }

    pub(crate) fn validate_enable_pk_index(&self) -> Result<()> {
        if !self.enable_pk_index {
            return Ok(());
        }

        if self.r#type != SINK_TYPE_UPSERT {
            return Err(SinkError::Config(anyhow!(
                "`enable_pk_index` is only supported for upsert iceberg sink"
            )));
        }

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Change write_mode to 'merge-on-read' in the WITH clause
  2. Remove the write_mode option so the default (merge-on-read) applies
  3. If copy-on-write is genuinely required, create the sink without the append-only type (upsert-compatible setup) — though for append-only data this is unnecessary

Example fix

// before
WITH (
  connector = 'iceberg',
  sink_type = 'append-only',
  write_mode = 'copy-on-write'
)
// after
WITH (
  connector = 'iceberg',
  sink_type = 'append-only',
  write_mode = 'merge-on-read'
)
Defensive patterns

Strategy: validation

Validate before calling

fn validate_sink_options(sink_type: &str, write_mode: &str) -> Result<(), String> {
    if sink_type == "append-only" && write_mode == "copy-on-write" {
        return Err(
            "append-only sink cannot use copy-on-write; use merge-on-read".to_string(),
        );
    }
    Ok(())
}

Type guard

fn is_invalid_append_only_combo(sink_type: &str, write_mode: IcebergWriteMode) -> bool {
    sink_type == "append-only" && write_mode == IcebergWriteMode::CopyOnWrite
}

Try / catch

match IcebergSinkConfig::validate_append_only_write_mode(sink_type, write_mode) {
    Err(e) => {
        // config conflict: switch write_mode to merge-on-read before creating the sink
        return Err(e.context("set write_mode = 'merge-on-read' for append-only sinks"));
    }
    Ok(()) => {}
}

Prevention

When it happens

Trigger: CREATE SINK ... WITH (connector='iceberg', sink_type='append-only' (or default append-only sink), write_mode='copy-on-write') — the combination is validated and rejected before the sink starts.

Common situations: Users copying write_mode settings from an upsert sink config; assuming copy-on-write is generally 'safer' or required for consistency; templates that set write_mode unconditionally regardless of sink_type.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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