risingwavelabs/risingwave · error · SinkError

The key encode is TEXT, but the primary key column {} has ty

Error message

The key encode is TEXT, but the primary key column {} has type {}. The key encode TEXT requires the primary key column to be of type varchar, bool, small int, int, big int, serial or rw_int256.

What it means

When key encode is TEXT, the primary key column must be one of the textual/integral types (varchar, bool, smallint, int, bigint, serial, int256) because the encoder renders it as a UTF-8 string. Floats and other types are deliberately rejected (see PR #16377 discussion on ambiguity of float key rendering).

Source

Thrown at src/connector/src/sink/formatter/mod.rs:270

            }
        }
    }
}

impl EncoderBuild for TextEncoder {
    async fn build(params: EncoderParams<'_>, pk_indices: Option<Vec<usize>>) -> Result<Self> {
        let (pk_index, schema_ref) = ensure_only_one_pk("TEXT", &params, &pk_indices)?;
        match &schema_ref.data_type() {
            DataType::Varchar
            | DataType::Boolean
            | DataType::Int16
            | DataType::Int32
            | DataType::Int64
            | DataType::Int256
            | DataType::Serial => {}
            _ => {
                // why we don't allow float as text for key encode: https://github.com/risingwavelabs/risingwave/pull/16377#discussion_r1591864960
                return Err(SinkError::Config(anyhow!(
                    "The key encode is TEXT, but the primary key column {} has type {}. The key encode TEXT requires the primary key column to be of type varchar, bool, small int, int, big int, serial or rw_int256.",
                    schema_ref.name,
                    schema_ref.data_type
                )));
            }
        }

        Ok(Self::new(params.schema, pk_index))
    }
}

impl EncoderBuild for AvroEncoder {
    async fn build(b: EncoderParams<'_>, pk_indices: Option<Vec<usize>>) -> Result<Self> {
        use crate::schema::{SchemaLoader, SchemaVersion};

        let loader = SchemaLoader::from_format_options(b.topic, &b.format_desc.options)
            .await
            .map_err(|e| SinkError::Config(anyhow!(e)))?;

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Cast the key column to varchar in the sink query
  2. Restructure so the key is an integer or varchar column
  3. Use a binary key encode (e.g. AVRO) if the key must preserve non-textual types

Example fix

// before: t.ts is TIMESTAMP, key_encode='text'
CREATE SINK s FROM t WITH (key_encode = 'text');
// after
CREATE SINK s AS SELECT ts::varchar AS ts_key, * FROM t WITH (key_encode = 'text');
Defensive patterns

Strategy: validation

Validate before calling

const TEXT_KEY_TYPES: &[DataType] = &[Varchar, Boolean, Int16, Int32, Int64, Serial, Int256];
if key_encode == "text" && !TEXT_KEY_TYPES.contains(&pk_field.data_type) {
    return Err("pk type not supported by text key encode");
}

Type guard

fn supports_text_key(dt: &DataType) -> bool {
    matches!(dt, DataType::Varchar | DataType::Boolean | DataType::Int16 | DataType::Int32 | DataType::Int64 | DataType::Serial | DataType::Int256)
}

Prevention

When it happens

Trigger: KEY ENCODE TEXT configured while the single PK column resolves to an unsupported DataType (e.g. Float64, Decimal, Timestamp, Struct).

Common situations: Using a float or timestamp primary key with a Kafka text key; expecting decimal keys to round-trip losslessly.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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