risingwavelabs/risingwave · error · SinkError::Config

`enable_pk_index` cannot be true when `force_append_only` is

Error message

`enable_pk_index` cannot be true when `force_append_only` is true

What it means

This error is raised when both `enable_pk_index = true` and `force_append_only = true` are set on an Iceberg sink. `force_append_only` converts the sink to append-only output (no upserts/deletes), which directly contradicts the pk index's purpose of supporting key-based upserts; the combination is rejected in `validate_enable_pk_index` (src/connector/src/sink/iceberg/config.rs:562).

Source

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

            return Err(SinkError::Config(anyhow!(
                "`enable_pk_index` is only supported for upsert iceberg sink"
            )));
        }

        if self.write_mode != IcebergWriteMode::MergeOnRead {
            return Err(SinkError::Config(anyhow!(
                "`enable_pk_index` is only supported for upsert iceberg sink with merge-on-read mode"
            )));
        }

        if self.format_version < FormatVersion::V2 {
            return Err(SinkError::Config(anyhow!(
                "`enable_pk_index` is only supported for upsert iceberg sink with format version >= 2"
            )));
        }

        if self.force_append_only {
            return Err(SinkError::Config(anyhow!(
                "`enable_pk_index` cannot be true when `force_append_only` is true"
            )));
        }

        Ok(())
    }

    pub fn from_btreemap(values: BTreeMap<String, String>) -> Result<Self> {
        let mut config =
            serde_json::from_value::<IcebergConfig>(serde_json::to_value(&values).unwrap())
                .map_err(|e| SinkError::Config(anyhow!(e)))?;

        if config.enable_compaction && !values.contains_key(COMPACTION_MAX_SNAPSHOTS_NUM) {
            config.max_snapshots_num_before_compaction = Some(DEFAULT_COMPACTION_MAX_SNAPSHOTS_NUM);
        }

        if config.r#type != SINK_TYPE_APPEND_ONLY && config.r#type != SINK_TYPE_UPSERT {
            return Err(SinkError::Config(anyhow!(

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Remove `force_append_only = true` if upsert semantics with the pk index are desired.
  2. Or remove `enable_pk_index = true` if append-only output is truly what is needed — the index is meaningless there.

Example fix

-- before
WITH (
  connector = 'iceberg',
  type = 'upsert',
  write_mode = 'merge-on-read',
  format_version = 2,
  force_append_only = true,
  enable_pk_index = true
);

-- after
WITH (
  connector = 'iceberg',
  type = 'upsert',
  write_mode = 'merge-on-read',
  format_version = 2,
  enable_pk_index = true
);
Defensive patterns

Strategy: validation

Validate before calling

-- reject the combination before DDL:
-- enable_pk_index = true AND force_append_only = true  -> invalid

Try / catch

-- on error, decide which flag to drop: keep force_append_only OR enable_pk_index, never both

Prevention

When it happens

Trigger: Creating an Iceberg sink where the WITH options contain both `enable_pk_index = true` and `force_append_only = true`. Fails during `validate_enable_pk_index` regardless of sink type or write mode, because this is the last check in the chain.

Common situations: Users forcing append-only output from an upsert stream (e.g. to avoid delete files) while a pk-index flag from a previous template remains; combining feature flags without realizing they are mutually exclusive.

Related errors


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