{"record":{"id":"5ceeb1f41bfa3764","repo":"risingwavelabs/risingwave","slug":"channel-and-channel-column-only-one-can-be-5ceeb1","errorCode":null,"errorMessage":"`{CHANNEL}` and `{CHANNEL_COLUMN}` only one can be set","messagePattern":"`(.+?)` and `(.+?)` only one can be set","errorType":"validation","errorClass":"SinkError::Config","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/redis.rs","lineNumber":427,"sourceCode":"                        )));\n                    }\n                    if let Some(member_type) = pk_map.get(member_name)\n                        && member_type == &DataType::Varchar\n                    {\n                        // do nothing\n                    } else {\n                        return Err(SinkError::Config(anyhow!(\n                            \"`{MEMBER_NAME}` must be set to `varchar` and `primary_key`\"\n                        )));\n                    }\n                }\n                Some(REDIS_VALUE_TYPE_PUBSUB) => {\n                    let channel = self.format_desc.options.get(CHANNEL);\n                    let channel_column = self.format_desc.options.get(CHANNEL_COLUMN);\n                    if (channel.is_none() && channel_column.is_none())\n                        || (channel.is_some() && channel_column.is_some())\n                    {\n                        return Err(SinkError::Config(anyhow!(\n                            \"`{CHANNEL}` and `{CHANNEL_COLUMN}` only one can be set\"\n                        )));\n                    }\n\n                    if let Some(channel_column) = channel_column\n                        && let Some(channel_column_type) = all_map.get(channel_column)\n                        && (channel_column_type != &DataType::Varchar)\n                    {\n                        return Err(SinkError::Config(anyhow!(\n                            \"`{CHANNEL_COLUMN}` must be set to `varchar`\"\n                        )));\n                    }\n\n                    let value_format =\n                        self.format_desc.options.get(VALUE_FORMAT).ok_or_else(|| {\n                            SinkError::Config(anyhow!(\"Cannot find `{VALUE_FORMAT}`\"))\n                        })?;\n                    TemplateStringEncoder::check_string_format(value_format, &all_map)?;","sourceCodeStart":409,"sourceCodeEnd":445,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/redis.rs#L409-L445","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"-- before (both set)\n... encode template (redis_value_type = 'pubsub', channel = 'events', channel_column = 'topic')\n-- after (dynamic per-row channel)\n... encode template (redis_value_type = 'pubsub', channel_column = 'topic')","handlingStrategy":"validation","validationCode":"-- guard before CREATE SINK (redis_value_type='pubsub'):\n-- has_channel = options.contains_key(\"channel\"); has_cc = options.contains_key(\"channel_column\")\n-- require has_channel XOR has_cc; if has_cc, schema[options[\"channel_column\"]] == \"character varying\"","typeGuard":null,"tryCatchPattern":"match result.err() { Some(e) if e.contains(\"channel\") => pick_exactly_one_of(channel, channel_column), _ => propagate(result) }","preventionTips":["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"],"tags":["redis","sink","pubsub","mutually-exclusive-options"],"backgroundTag":"conflicting-config-options","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}