risingwavelabs/risingwave · error · SinkError::Config
Must set `primary-key` in {}
Error message
Must set `primary-key` in {} What it means
This error is raised by IcebergSinkConfig::from_btreemap when sink type is 'upsert' but the `primary_key` option is entirely absent and pk-index auto-derivation is not enabled. An upsert Iceberg sink must know which columns form the primary key to merge rows; without it the configuration is invalid.
Source
Thrown at src/connector/src/sink/iceberg/config.rs:602
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 out
// in WITH options. The derived pk is written back into properties before
// this validation is consulted again at sink-construction time.
return Err(SinkError::Config(anyhow!(
"Must set `primary-key` in {}",
SINK_TYPE_UPSERT
)));
}
}
// Enforce merge-on-read for append-only sinks
Self::validate_append_only_write_mode(&config.r#type, config.write_mode)?;
config.validate_enable_pk_index()?;
config.validate_manifest_rewrite_format(config.format_version)?;
// All configs start with "catalog." will be treated as java configs.
config.java_catalog_props = iceberg_java_catalog_props_from_options(
values
.iter()
.map(|(key, value)| (key.as_str(), value.as_str())),
);
configView on GitHub (pinned to 6469eb736d)
Solutions
- Add `primary_key='<col1,col2>'` to the WITH options naming the upsert key columns.
- Set `enable_pk_index=true` in the WITH options so the planner derives the Iceberg pk from the upstream stream key.
- Change `type` to `append-only` if upsert semantics are not needed.
Example fix
// before WITH (connector = 'iceberg', type = 'upsert') // after WITH (connector = 'iceberg', type = 'upsert', primary_key = 'order_id')
Defensive patterns
Strategy: validation
Validate before calling
if (opts.type === 'upsert' && !opts.primary_key && !opts.enable_pk_index) {
throw new Error("Must set `primary-key` in upsert");
} Type guard
function upsertKeyReady(opts) {
return (typeof opts.primary_key === 'string' && opts.primary_key.length > 0) || opts.enable_pk_index === true;
} Try / catch
try {
await createIcebergSink(opts);
} catch (e) {
if (String(e).includes('Must set `primary-key`')) {
opts.enable_pk_index = true;
return createIcebergSink(opts);
}
throw e;
} Prevention
- Always pair type='upsert' with either primary_key or enable_pk_index=true.
- Lint generated sink DDL for required upsert options.
- Derive the pk from the stream key at DDL-generation time.
When it happens
Trigger: CREATE SINK with `type='upsert'` on an Iceberg connector where the WITH options contain no `primary_key` and `enable_pk_index` is false/unset.
Common situations: Users forgetting the `primary_key` option when converting an append-only sink to upsert; older DDL scripts predating `enable_pk_index`; schemas where the upstream has no natural key.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- `primary-key` must not be empty in {}
- `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
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/93fe7ce88908dc76.
Report an issue: GitHub.