risingwavelabs/risingwave · error · SinkError

config error: {0}

Error message

config error: {0}

What it means

`SinkError::Config` wraps an `anyhow::Error` and is thrown when a sink's user-supplied configuration fails validation or cannot be parsed into the connector's config struct. Displayed as "config error: {0}" with the original cause and backtrace retained. It separates configuration problems from runtime data-path failures.

Source

Thrown at src/connector/src/sink/mod.rs:1114

        anyhow::Error,
    ),
    #[error("Remote sink error: {0}")]
    Remote(
        #[source]
        #[backtrace]
        anyhow::Error,
    ),
    #[error("Encode error: {0}")]
    Encode(String),
    #[error("Avro error: {0}")]
    Avro(#[from] apache_avro::Error),
    #[error("Iceberg error: {0}")]
    Iceberg(
        #[source]
        #[backtrace]
        anyhow::Error,
    ),
    #[error("config error: {0}")]
    Config(
        #[source]
        #[backtrace]
        anyhow::Error,
    ),
    #[error("coordinator error: {0}")]
    Coordinator(
        #[source]
        #[backtrace]
        anyhow::Error,
    ),
    #[error("ClickHouse error: {0}")]
    ClickHouse(String),
    #[error("Redis error: {0}")]
    Redis(String),
    #[error("Http error: {0}")]
    Http(
        #[source]

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Read the wrapped message — serde errors name the exact unknown/missing/mistyped field.
  2. Cross-check every `WITH` option against the connector's documented properties and fix spelling/types.
  3. Add any required fields (endpoint, bucket, region, auth credentials, connection name) that the message flags as missing.
  4. If using a `CREATE CONNECTION`/private-link reference, confirm the referenced connection exists and is accessible.

Example fix

-- before
CREATE SINK s FROM mv WITH (
  connector = 'kafka',
  properties = 'broker:9092'
);
-- after
CREATE SINK s FROM mv WITH (
  connector = 'kafka',
  properties.broker = 'broker:9092',
  topic = 'my-topic'
);
Defensive patterns

Strategy: validation

Validate before calling

fn validate_sink_options(opts: &serde_json::Value, required: &[&str]) -> Result<(), String> {
    for key in required {
        if opts.get(key).map(|v| !v.is_null()).unwrap_or(false) {
            continue;
        }
        return Err(format!("missing required sink option '{key}'"));
    }
    Ok(())
}

Type guard

fn as_config_error(err: &SinkError) -> Option<&anyhow::Error> {
    if let SinkError::Config(e) = err { Some(e) } else { None }
}

Try / catch

match SinkWriter::new(sink, opts).await {
    Err(SinkError::Config(e)) => {
        log::error!("bad sink config: {e:#}");
        // surface a user-facing 'fix your WITH options' message; do not retry
    }
    Err(e) => return Err(e.into()),
    Ok(w) => { /* proceed */ }
}

Prevention

When it happens

Trigger: Creating a sink: parsing `WITH` options into the connector-specific config struct (serde deserialization), validating required fields, or any explicit config check that returns `anyhow!(...)` converted into this variant.

Common situations: Misspelled or unknown keys in the `WITH` clause; missing required fields (e.g. no `endpoint`, `bucket`, or `connection.name`); wrong value types (string where number/bool expected); referencing a private-link/connection that does not exist; env-dependent defaults resolving to invalid values.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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