risingwavelabs/risingwave · error

VARIANT column `{}` cannot be used as the primary key of an

Error message

VARIANT column `{}` cannot be used as the primary key of an upsert iceberg sink

What it means

For upsert (primary-key) Iceberg sinks, the primary key columns must be stored as Iceberg identity fields, but Iceberg does not support VARIANT (semi-structured) columns as identity/primary-key fields. Validation therefore rejects any sink whose PK includes a column containing a VARIANT type.

Source

Thrown at src/connector/src/sink/iceberg/mod.rs:260

            self.config.write_mode,
        )?;
        validate_explicit_compaction_type(&self.config)?;
        validate_compaction_option_compatibility(&self.config)?;

        // VARIANT is not comparable, so it can never be an equality-delete key.
        if self.config.r#type == SINK_TYPE_UPSERT
            && !self.config.force_append_only
            && let Some(pk_indices) = self
                .param
                .downstream_pk
                .as_ref()
                .filter(|pk| !pk.is_empty())
        {
            for &idx in pk_indices {
                if let Some(column) = self.param.columns.get(idx)
                    && column.data_type.contains_variant()
                {
                    bail!(
                        "VARIANT column `{}` cannot be used as the primary key of an upsert iceberg sink",
                        column.name
                    );
                }
            }
        }

        let table = self.create_and_validate_table().await?;
        self.config
            .validate_manifest_rewrite_format(table.metadata().format_version())?;
        Ok(())
    }

    fn support_schema_change() -> bool {
        true
    }

    fn validate_alter_config_change(

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Choose a primary key made of non-variant columns (integers, strings, timestamps, etc.).
  2. Extract the key into a concrete typed column in the MV and use it as the sink PK.
  3. Change the sink to an append-only sink (no primary key) if upsert semantics are not required.

Example fix

-- before
CREATE SINK s FROM mv WITH (connector='iceberg', primary_key='payload'); -- payload is jsonb
-- after
CREATE SINK s FROM mv WITH (connector='iceberg', primary_key='id'); -- id is BIGINT
Defensive patterns

Strategy: validation

Validate before calling

-- ensure no PK column is jsonb/variant
SELECT c.name
FROM rw_columns c
WHERE c.relation_id = <sink_upstream_mv_id>
  AND c.name = ANY(ARRAY[<pk_columns>])
  AND c.data_type IN ('jsonb', 'variant');

Prevention

When it happens

Trigger: Creating an upsert Iceberg sink whose primary key includes a `jsonb`/VARIANT column, including via nested `contains_variant()` types (e.g. struct fields containing variant).

Common situations: Streaming CDC tables where the natural key was modeled as jsonb; users materializing a deduplicated table keyed on a variant payload instead of concrete key columns.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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