risingwavelabs/risingwave · error · SinkError::Config
`{}` must be {}, or {}
Error message
`{}` must be {}, or {} What it means
This error is raised by `IcebergConfig::from_btreemap` (src/connector/src/sink/iceberg/config.rs:580) when the `type` sink option is neither `'append-only'` nor `'upsert'`. The Iceberg sink supports exactly these two write types; any other value (including typos and empty strings) is rejected with `SinkError::Config` naming the option and the two allowed values.
Source
Thrown at src/connector/src/sink/iceberg/config.rs:581
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!(
"`{}` must be {}, or {}",
SINK_TYPE_OPTION,
SINK_TYPE_APPEND_ONLY,
SINK_TYPE_UPSERT
)));
}
if config.r#type == SINK_TYPE_UPSERT {
if let Some(primary_key) = &config.primary_key {
if primary_key.is_empty() {
return Err(SinkError::Config(anyhow!(
"`primary-key` must not be empty in {}",
SINK_TYPE_UPSERT
)));
}
} else if !config.enable_pk_index {
// When `enable_pk_index = true`, the planner auto-derives the iceberg pk
// from the upstream stream key, so the user does not need to spell it outView on GitHub (pinned to 6469eb736d)
Solutions
- Set `type = 'append-only'` or `type = 'upsert'` (exact lowercase spelling) in the sink WITH options.
- Remove the `type` option entirely if the default is acceptable and no custom value is needed.
- Check the exact string against SINK_TYPE_APPEND_ONLY / SINK_TYPE_UPSERT constants — values are matched case-sensitively, no normalization is applied.
Example fix
-- before WITH ( connector = 'iceberg', type = 'UPSERT' ); -- after WITH ( connector = 'iceberg', type = 'upsert' );
Defensive patterns
Strategy: validation
Validate before calling
-- allowed values for the iceberg sink type option:
-- type in ('append-only', 'upsert') -- exact, lowercase, no underscores Try / catch
-- SinkError::Config at CREATE SINK time; fix `type` to one of the listed values and retry
Prevention
- Copy type values only from RisingWave Iceberg sink docs, not other connectors.
- Use exact lowercase literals; no case normalization is applied.
- Let the option default when no custom behavior is needed.
When it happens
Trigger: Creating an Iceberg sink with `type` set to any value other than 'append-only' or 'upsert' — e.g. `type = 'upsert'` misspelled as 'upsert-only'/'Upsert' (values are compared case-sensitively), `type = ''`, or a type copied from a different connector (e.g. 'append_only' with an underscore).
Common situations: Typo in the WITH clause; copying `type` values from other sink connectors with different vocabularies; case mismatch ('UPSERT' vs 'upsert'); omitting the value's quotes so an unexpected literal lands in the map.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
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
- `enable_pk_index` cannot be true when `force_append_only` is
- `primary-key` must not be empty in {}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/d2960c8e47d9a07f.
Report an issue: GitHub.