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
- Remove `force_append_only = true` if upsert semantics with the pk index are desired.
- 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
- Treat `enable_pk_index` and `force_append_only` as mutually exclusive options.
- When templating, gate `enable_pk_index` behind upsert profiles only.
- Read the validation chain in config.rs:539 to see the full set of constraints in order.
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
- `enable_pk_index` is only supported for upsert iceberg sink
- `enable_pk_index` is only supported for upsert iceberg sink
- `enable_pk_index` is only supported for upsert iceberg sink
- `{}` must be {}, or {}
- `primary-key` must not be empty in {}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/b063177a5dbbf98e.
Report an issue: GitHub.