{"record":{"id":"d488ef2a043527c2","repo":"risingwavelabs/risingwave","slug":"e-d488ef","errorCode":null,"errorMessage":"{e}","messagePattern":"\\{e\\}","errorType":"error_code","errorClass":"SinkError::Config","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/google_pubsub.rs","lineNumber":116,"sourceCode":"    #[serde(rename = \"pubsub.credentials\")]\n    pub credentials: Option<String>,\n\n    #[serde(flatten)]\n    pub unknown_fields: std::collections::HashMap<String, String>,\n}\n\ncrate::impl_sink_unknown_fields!(GooglePubSubConfig);\n\nimpl EnforceSecret for GooglePubSubConfig {\n    const ENFORCE_SECRET_PROPERTIES: phf::Set<&'static str> = phf::phf_set! {\n        \"pubsub.credentials\",\n    };\n}\n\nimpl GooglePubSubConfig {\n    fn from_btreemap(values: BTreeMap<String, String>) -> Result<Self> {\n        serde_json::from_value::<GooglePubSubConfig>(serde_json::to_value(values).unwrap())\n            .map_err(|e| SinkError::Config(anyhow!(e)))\n    }\n}\n\n#[derive(Clone, Debug)]\npub struct GooglePubSubSink {\n    pub config: GooglePubSubConfig,\n    is_append_only: bool,\n\n    schema: Schema,\n    pk_indices: Vec<usize>,\n    format_desc: SinkFormatDesc,\n    db_name: String,\n    sink_from_name: String,\n}\n\nimpl EnforceSecret for GooglePubSubSink {\n    fn enforce_secret<'a>(\n        prop_iter: impl Iterator<Item = &'a str>,","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/google_pubsub.rs#L98-L134","documentation":"GooglePubSubConfig::from_btreemap converts the sink's string properties map into a JSON object and deserializes it into GooglePubSubConfig. Any field with an unexpected type or unknown/invalid value makes serde fail, and the raw serde error message is surfaced as this SinkError::Config.","triggerScenarios":"Creating a Google Pub/Sub sink whose WITH options include a mistyped field (e.g. `endpoint` given a non-string, or an option name that doesn't exist on GooglePubSubConfig via deny_unknown_fields), so serde_json::from_value fails.","commonSituations":"Typos in pubsub option names like `pubsub.emulator_host` or `pubsub.credentials`; passing numbers/booleans where strings are expected; stale option names after a config struct change.","solutions":["Read the embedded serde message (the `{e}`) to see which field failed and fix that option name/value","Compare your WITH options against the GooglePubSubConfig struct fields in src/connector/src/sink/google_pubsub.rs","Ensure all option values are quoted strings in the sink definition"],"exampleFix":"// before\nWITH (connector='google_pubsub', emulator_host=123)\n// after\nWITH (connector='google_pubsub', pubsub.emulator_host='localhost:8085')","handlingStrategy":"validation","validationCode":"fn validate_pubsub_props(props: &[(String, String)]) -> Result<(), String> {\n    let allowed = [\"pubsub.emulator_host\", \"pubsub.credentials\", \"pubsub.topic\", \"pubsub.endpoint\"];\n    for (k, v) in props {\n        if !allowed.contains(&k.as_str()) {\n            return Err(format!(\"unknown pubsub option: {k}\"));\n        }\n        if v.is_empty() {\n            return Err(format!(\"empty value for pubsub option: {k}\"));\n        }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Quote all WITH option values as strings","Keep option names in sync with the GooglePubSubConfig struct","Read the chained serde message which names the offending field"],"tags":["pubsub","serde","config"],"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-14T16:17:12.679Z"}