risingwavelabs/risingwave · error · SinkError::Config

Cannot find

Error message

Cannot find `{VALUE_FORMAT}`

What it means

For the Redis sink with a value type that requires a template (string and stream paths), `validate` requires the `value format` option to be present in the format descriptor options. If it is absent, validation returns this config error, because the sink cannot know how to encode row values into the Redis value.

Solutions

  1. Add `value format = '<template with placeholders like {{col}}>'` to the sink options.
  2. Check the option key spelling — it must be exactly `value format` (SPACE_FORMAT style keys are space separated).
  3. Use `key format`/`value format` templates consistent with TemplateStringEncoder's supported syntax.

Example fix

// before
... WITH (connector='redis', value type='string')
// after
... WITH (connector='redis', value type='string', value format='{{k}}={{v}}')
Defensive patterns

Strategy: validation

Validate before calling

if (opts['value type'] === 'string' && !opts['value format']) throw new Error('value format is required for redis string sink');

Try / catch

try { await createSink(cfg); } catch (e) { if (/Cannot find `value format`/.test(String(e))) cfg['value format'] = promptTemplate(); else throw e; }

Prevention

When it happens

Trigger: Create a Redis sink with `value type = 'string'` (or omitted so the string branch runs, or 'stream') without supplying `value format = '<template>'` in the options.

Common situations: Users forget the `value format` option entirely, or misspell it (e.g. `value_format`, `value-format`); also occurs when copying a JSON-format sink config that never needed it.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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

Appendix: source

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

                        || (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)?;
                }
                Some(REDIS_VALUE_TYPE_STREAM) => {
                    tracing::error!("test:for bug");
                    risingwave_common::license::Feature::RedisSinkStream
                        .check_available()
                        .map_err(|e| anyhow::anyhow!(e))?;
                    let stream = self.format_desc.options.get(STREAM);
                    let stream_column = self.format_desc.options.get(STREAM_COLUMN);
                    if (stream.is_none() && stream_column.is_none())
                        || (stream.is_some() && stream_column.is_some())
                    {
                        return Err(SinkError::Config(anyhow!(
                            "Please specific either `{STREAM}` or `{STREAM_COLUMN}`. They are mutually exclusive options."
                        )));
                    }

View on GitHub (pinned to 6469eb736d)