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
- Keep exactly one: remove `channel` if you want per-row dynamic channels via `channel_column`, or remove `channel_column` to use the fixed `channel`
- If neither was set, add `channel = '<name>'` (or `channel_column = '<col>'`) to the options
- 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
- Decide upfront between a static channel and a per-row channel column; set only one
- Remove stale `channel` options when migrating to dynamic channel_column
- Remember channel_column must reference a varchar column
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
- Cannot find ' ',please set it.
- ` ` and ` ` only one can be set
- Cannot find ` `,please set it.
- Cannot find ' ',please set it.
- Cannot find ' ', please set it or use JSON
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)