risingwavelabs/risingwave · error · SinkError::Config

` ` and ` ` only one can be set

Error message

`{CHANNEL}` and `{CHANNEL_COLUMN}` only one can be set

What it means

A Redis pubsub sink (template encoding, redis_value_type='pubsub') must target its messages via exactly one of two options: a static `channel` string or a `channel_column` taken from a schema column — and at least one must be given. RedisSink::validate raises this SinkError::Config when both are set, or when neither is set, since the destination channel would be ambiguous or undefined.

Solutions

  1. Keep exactly one: remove `channel` if you want per-row dynamic channels via `channel_column`, or remove `channel_column` to use the fixed `channel`
  2. If neither was set, add `channel = '<name>'` (or `channel_column = '<col>'`) to the options
  3. Note channel_column, when used, must reference a varchar column

Example fix

-- before (both set)
... encode template (redis_value_type = 'pubsub', channel = 'events', channel_column = 'topic')
-- after (dynamic per-row channel)
... encode template (redis_value_type = 'pubsub', channel_column = 'topic')
Defensive patterns

Strategy: validation

Validate before calling

-- guard before CREATE SINK (redis_value_type='pubsub'):
-- has_channel = options.contains_key("channel"); has_cc = options.contains_key("channel_column")
-- require has_channel XOR has_cc; if has_cc, schema[options["channel_column"]] == "character varying"

Try / catch

match result.err() { Some(e) if e.contains("channel") => pick_exactly_one_of(channel, channel_column), _ => propagate(result) }

Prevention

When it happens

Trigger: CREATE SINK with encode template and redis_value_type = 'pubsub', where both `channel` and `channel_column` are present in the options, or where both are absent (src/connector/src/sink/redis.rs:421-430).

Common situations: Copying a config that already has `channel` and then adding `channel_column` for dynamic routing; upgrading from a static-channel example to per-row channels without removing `channel`; omitting both because they seem optional.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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

Appendix: source

Thrown at src/connector/src/sink/redis.rs:427

                        )));
                    }
                    if let Some(member_type) = pk_map.get(member_name)
                        && member_type == &DataType::Varchar
                    {
                        // do nothing
                    } else {
                        return Err(SinkError::Config(anyhow!(
                            "`{MEMBER_NAME}` must be set to `varchar` and `primary_key`"
                        )));
                    }
                }
                Some(REDIS_VALUE_TYPE_PUBSUB) => {
                    let channel = self.format_desc.options.get(CHANNEL);
                    let channel_column = self.format_desc.options.get(CHANNEL_COLUMN);
                    if (channel.is_none() && channel_column.is_none())
                        || (channel.is_some() && channel_column.is_some())
                    {
                        return Err(SinkError::Config(anyhow!(
                            "`{CHANNEL}` and `{CHANNEL_COLUMN}` only one can be set"
                        )));
                    }

                    if let Some(channel_column) = channel_column
                        && let Some(channel_column_type) = all_map.get(channel_column)
                        && (channel_column_type != &DataType::Varchar)
                    {
                        return Err(SinkError::Config(anyhow!(
                            "`{CHANNEL_COLUMN}` must be set to `varchar`"
                        )));
                    }

                    let value_format =
                        self.format_desc.options.get(VALUE_FORMAT).ok_or_else(|| {
                            SinkError::Config(anyhow!("Cannot find `{VALUE_FORMAT}`"))
                        })?;
                    TemplateStringEncoder::check_string_format(value_format, &all_map)?;

View on GitHub (pinned to 6469eb736d)