{"record":{"id":"a56c12d3ef902482","repo":"risingwavelabs/risingwave","slug":"serde-json-deserialization-error-for-postgresconf","errorCode":null,"errorMessage":"{serde_json deserialization error for PostgresConfig from properties}","messagePattern":"\\{serde_json deserialization error for PostgresConfig from properties\\}","errorType":"validation","errorClass":"SinkError::Config","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/postgres.rs","lineNumber":154,"sourceCode":"        .context(\"failed to check foreign key constraints\")?\n        .get::<_, bool>(0);\n\n    if has_foreign_key {\n        return Err(SinkError::Config(anyhow!(\n            \"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.\",\n            schema,\n            table,\n        )));\n    }\n\n    Ok(())\n}\n\nimpl PostgresConfig {\n    pub fn from_btreemap(properties: BTreeMap<String, String>) -> Result<Self> {\n        let config =\n            serde_json::from_value::<PostgresConfig>(serde_json::to_value(properties).unwrap())\n                .map_err(|e| SinkError::Config(anyhow!(e)))?;\n        if config.r#type != SINK_TYPE_APPEND_ONLY && config.r#type != SINK_TYPE_UPSERT {\n            return Err(SinkError::Config(anyhow!(\n                \"`{}` must be {}, or {}\",\n                SINK_TYPE_OPTION,\n                SINK_TYPE_APPEND_ONLY,\n                SINK_TYPE_UPSERT\n            )));\n        }\n        Ok(config)\n    }\n\n    pub fn pg_connection_config(&self) -> PgConnectionConfig {\n        PgConnectionConfig {\n            host: self.host.clone(),\n            port: self.port,\n            user: self.user.clone(),\n            password: self.password.clone(),\n            database: self.database.clone(),","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/postgres.rs#L136-L172","documentation":"`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.","triggerScenarios":"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).","commonSituations":"Typos in option names (`tablename` vs `table`); missing `type` option; passing the connection string in the wrong field; quoting problems in CREATE SINK options.","solutions":["Check the serde detail in the error output — it names the offending field","Verify required options: host, port, database, schema, table, username, password, and `type`","Fix option names and value types to match PostgresConfig's expected fields","Validate by re-running CREATE SINK after corrections"],"exampleFix":"// before\nWITH (connector='postgres', host='pg', port='5432', tablename='t', type='upsert');\n// after\nWITH (connector='postgres', host='pg', port='5432', database='db', schema='public', table='t', user='u', password='p', type='upsert');","handlingStrategy":"validation","validationCode":"let required = [\"host\", \"port\", \"database\", \"schema\", \"table\", \"username\", \"password\", \"type\"];\nfor k in required { assert!(options.contains_key(k), \"missing postgres option: {}\", k); }\nassert!(options[\"port\"].parse::<u16>().is_ok(), \"port must be numeric\");\nassert!(matches!(options[\"type\"].as_str(), \"append-only\" | \"upsert\"));","typeGuard":null,"tryCatchPattern":"match PostgresConfig::from_btreemap(opts.clone()) {\n    Ok(cfg) => cfg,\n    Err(SinkError::Config(e)) => return Err(format!(\"bad postgres options: {e}\")),\n    Err(e) => return Err(format!(\"postgres sink error: {e}\")),\n}","preventionTips":["Verify every documented option name before running CREATE SINK","Keep port as a plain integer string","Quote option values containing spaces or special characters"],"tags":["postgres","sink","config","serde"],"backgroundTag":"config-type-mismatch","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"}