risingwavelabs/risingwave · error · SinkError::Config

{serde_json URL parse error for RedisCommon}

Error message

{serde_json URL parse error for RedisCommon}

What it means

RedisCommon::build_conn_and_pipe parses the sink's url field as JSON (either a single URL string or an array of URLs) with serde_json::from_str, mapping any parse failure to SinkError::Config. The library throws it because the URL(s) supplied in the sink options must be valid JSON strings to build Redis connections.

Source

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

                RedisSinkPayloadWriterInput::RedisGeoKey((key, member)) => {
                    pipe.zrem(key, member);
                }
                _ => return Err(SinkError::Redis("RedisPipe del not match".to_owned())),
            },
        };
        Ok(())
    }
}
pub enum RedisConn {
    // Redis deployed as a cluster, clusters with only one node should also use this conn
    Cluster(ClusterConnection),
    // Redis is not deployed as a cluster
    Single(MultiplexedConnection),
}

impl RedisCommon {
    pub async fn build_conn_and_pipe(&self) -> ConnectorResult<(RedisConn, RedisPipe)> {
        match serde_json::from_str(&self.url).map_err(|e| SinkError::Config(anyhow!(e))) {
            Ok(v) => {
                if let Value::Array(list) = v {
                    let list = list
                        .into_iter()
                        .map(|s| {
                            if let Value::String(s) = s {
                                Ok(s)
                            } else {
                                Err(SinkError::Redis(
                                    "redis.url must be array of string".to_owned(),
                                )
                                .into())
                            }
                        })
                        .collect::<ConnectorResult<Vec<String>>>()?;

                    let client = ClusterClient::new(list)?;
                    Ok((

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Wrap the URL in quotes so it is valid JSON: "redis://127.0.0.1:6379".
  2. If multiple URLs are supported, pass a JSON array of quoted strings: ["redis://host1:6379","redis://host2:6379"].
  3. Validate the url value with serde_json::from_str before creating the sink.
  4. Check SQL escaping — inner double quotes may need to survive the WITH option parser.

Example fix

// before
url = 'redis://127.0.0.1:6379'
// after
url = '"redis://127.0.0.1:6379"'
Defensive patterns

Strategy: validation

Validate before calling

// Validate the url option is valid JSON before creating the sink
fn validate_redis_url(url: &str) -> Result<(), serde_json::Error> {
    serde_json::from_str::<serde_json::Value>(url).map(|_| ())
}

Type guard

fn is_json_string_or_string_array(s: &str) -> bool {
    serde_json::from_str::<serde_json::Value>(s)
        .map(|v| v.is_string() || matches!(&v, serde_json::Value::Array(a) if a.iter().all(|x| x.is_string())))
        .unwrap_or(false)
}

Try / catch

match RedisCommon::build_conn_and_pipe(&common).await {
    Err(SinkError::Config(e)) => eprintln!("invalid redis url JSON: {e:#}"),
    Err(e) => return Err(e),
    Ok((conn, pipe)) => { /* proceed */ }
}

Prevention

When it happens

Trigger: Calling build_conn_and_pipe when RedisCommon.url is not valid JSON — e.g. a raw URL string 'redis://127.0.0.1:6379' without surrounding quotes, or a malformed array like [redis://a, redis://b].

Common situations: Users passing a plain Redis URL instead of a JSON-encoded string/array in WITH options; shell/SQL escaping stripping quotes; using a JSON array with non-string elements.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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