risingwavelabs/risingwave · error · SinkError

ENCODE TEXT is only valid as key encode.

Error message

ENCODE TEXT is only valid as key encode.

What it means

The generic sink formatter dispatcher only allows ENCODE TEXT to serve as the KEY encoding (for key-only scenarios). When TEXT is chosen as the value encode for an AppendOnly or Upsert sink, it rejects the combination with this configuration error, because value serialization as plain text is not supported.

Source

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

                (F::Upsert, E::Avro, Some(E::Text)) => Impl::UpsertTextAvro(build(p).await?),
                (F::Upsert, E::Avro, Some(E::Bytes)) => {
                    Impl::UpsertBytesAvro(build(p).await?)
                }
                (F::Upsert, E::Avro, None) => Impl::UpsertAvro(build(p).await?),
                (F::Upsert, E::Protobuf, Some(E::Text)) => Impl::UpsertTextProto(build(p).await?),
                (F::Upsert, E::Protobuf, Some(E::Bytes)) => {
                    Impl::UpsertBytesProto(build(p).await?)
                }
                (F::Upsert, E::Template, Some(E::Text)) => {
                    Impl::UpsertTextTemplate(build(p).await?)
                }
                (F::Upsert, E::Template, Some(E::Bytes)) => {
                    Impl::UpsertBytesTemplate(build(p).await?)
                }
                (F::Upsert, E::Template, None) => Impl::UpsertTemplate(build(p).await?),
                (F::Debezium, E::Json, None) => Impl::DebeziumJson(build(p).await?),
                (F::AppendOnly | F::Upsert, E::Text, _) => {
                    return Err(SinkError::Config(anyhow!(
                        "ENCODE TEXT is only valid as key encode."
                    )));
                }
                (F::AppendOnly, E::Avro, _)
                | (F::Upsert, E::Protobuf, _)
                | (F::Upsert, E::Bytes, _)
                | (F::Debezium, E::Json, Some(_))
                | (F::Debezium, E::Avro | E::Protobuf | E::Template | E::Text | E::Bytes, _)
                | (_, E::Parquet, _)
                | (_, _, Some(E::Parquet))
                | (F::AppendOnly, E::Bytes, Some(_))
                | (F::AppendOnly | F::Upsert, _, Some(E::Template) | Some(E::Json) | Some(E::Avro) | Some(E::Protobuf)) // reject other encode as key encode
                => {
                    return Err(SinkError::Config(anyhow!(
                        "sink format/encode/key_encode unsupported: {:?} {:?} {:?}",
                        format_desc.format,
                        format_desc.encode,
                        format_desc.key_encode

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Move `ENCODE TEXT` to the key encode position (use `KEY ENCODE TEXT`) and pick a supported value encode such as JSON or Avro
  2. Use `ENCODE JSON` (or another supported encode) for the value if a full payload encode is needed
  3. Check the FORMAT...ENCODE...VALIDATE syntax in the RisingWave sink docs

Example fix

// before
CREATE SINK s FROM mv WITH (connector='kafka', FORMAT APPEND ONLY ENCODE TEXT);
// after
CREATE SINK s FROM mv WITH (connector='kafka', FORMAT APPEND ONLY ENCODE JSON KEY ENCODE TEXT);
Defensive patterns

Strategy: validation

Validate before calling

fn validate_format(format: &str, encode: &str, key_encode: Option<&str>) -> Result<(), String> {
    if encode.eq_ignore_ascii_case("text") {
        return Err("ENCODE TEXT is only valid as key encode".into());
    }
    Ok(())
}

Prevention

When it happens

Trigger: Running `CREATE SINK ... FORMAT ... ENCODE TEXT` (or with an explicit key_encode) for AppendOnly/Upsert formats where TEXT lands in the value encode slot rather than the key encode slot.

Common situations: Users copying a key-only sink example and applying `ENCODE TEXT` at the value position; confusion between `key_encode` and `encode` options in the sink definition.

Related errors


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