risingwavelabs/risingwave · error · SinkError::Config

{serde_json deserialization error for PostgresConfig from…

Error message

{serde_json deserialization error for PostgresConfig from properties}

What it means

`PostgresConfig::from_btreemap` deserializes the WITH options map into `PostgresConfig` via serde_json and wraps any deserialization failure as `SinkError::Config`. This generic message covers mistyped, missing, or unknown Postgres sink option fields.

Solutions

  1. Check the serde detail in the error output — it names the offending field
  2. Verify required options: host, port, database, schema, table, username, password, and `type`
  3. Fix option names and value types to match PostgresConfig's expected fields
  4. Validate by re-running CREATE SINK after corrections

Example fix

// before
WITH (connector='postgres', host='pg', port='5432', tablename='t', type='upsert');
// after
WITH (connector='postgres', host='pg', port='5432', database='db', schema='public', table='t', user='u', password='p', type='upsert');
Defensive patterns

Strategy: validation

Validate before calling

let required = ["host", "port", "database", "schema", "table", "username", "password", "type"];
for k in required { assert!(options.contains_key(k), "missing postgres option: {}", k); }
assert!(options["port"].parse::<u16>().is_ok(), "port must be numeric");
assert!(matches!(options["type"].as_str(), "append-only" | "upsert"));

Try / catch

match PostgresConfig::from_btreemap(opts.clone()) {
    Ok(cfg) => cfg,
    Err(SinkError::Config(e)) => return Err(format!("bad postgres options: {e}")),
    Err(e) => return Err(format!("postgres sink error: {e}")),
}

Prevention

When it happens

Trigger: Calling `PostgresConfig::from_btreemap(properties)` when the options map cannot be deserialized: missing required fields (host, port, database, table, `type`), wrong-typed values (e.g. non-numeric port), or unknown keys (when strict deserialization applies).

Common situations: Typos in option names (`tablename` vs `table`); missing `type` option; passing the connection string in the wrong field; quoting problems in CREATE SINK options.

Related errors


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

Appendix: source

Thrown at src/connector/src/sink/postgres.rs:154

        .context("failed to check foreign key constraints")?
        .get::<_, bool>(0);

    if has_foreign_key {
        return Err(SinkError::Config(anyhow!(
            "Postgres sink does not support target table \"{}\".\"{}\" with foreign key constraints. Please remove foreign key constraints from the target table or choose a different sink table.",
            schema,
            table,
        )));
    }

    Ok(())
}

impl PostgresConfig {
    pub fn from_btreemap(properties: BTreeMap<String, String>) -> Result<Self> {
        let config =
            serde_json::from_value::<PostgresConfig>(serde_json::to_value(properties).unwrap())
                .map_err(|e| SinkError::Config(anyhow!(e)))?;
        if config.r#type != SINK_TYPE_APPEND_ONLY && config.r#type != SINK_TYPE_UPSERT {
            return Err(SinkError::Config(anyhow!(
                "`{}` must be {}, or {}",
                SINK_TYPE_OPTION,
                SINK_TYPE_APPEND_ONLY,
                SINK_TYPE_UPSERT
            )));
        }
        Ok(config)
    }

    pub fn pg_connection_config(&self) -> PgConnectionConfig {
        PgConnectionConfig {
            host: self.host.clone(),
            port: self.port,
            user: self.user.clone(),
            password: self.password.clone(),
            database: self.database.clone(),

View on GitHub (pinned to 6469eb736d)