{"record":{"id":"9a51e744fab84bb4","repo":"risingwavelabs/risingwave","slug":"config-deserialization-error-e","errorCode":null,"errorMessage":"config deserialization error: {e}","messagePattern":"config deserialization error: (.+?)","errorType":"validation","errorClass":"SinkError","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/file_sink/azblob.rs","lineNumber":113,"sourceCode":"pub struct AzblobSink;\n\nimpl UnknownFields for AzblobConfig {\n    fn unknown_fields(&self) -> HashMap<String, String> {\n        self.unknown_fields.clone()\n    }\n}\n\ncrate::impl_sink_unknown_fields!(AzblobConfig);\n\nimpl OpendalSinkBackend for AzblobSink {\n    type Properties = AzblobConfig;\n\n    const SINK_NAME: &'static str = AZBLOB_SINK;\n\n    fn from_btreemap(btree_map: BTreeMap<String, String>) -> Result<Self::Properties> {\n        let config =\n            serde_json::from_value::<AzblobConfig>(serde_json::to_value(btree_map).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    fn new_operator(properties: AzblobConfig) -> Result<Operator> {\n        FileSink::<AzblobSink>::new_azblob_sink(properties)\n    }\n\n    fn get_path(properties: Self::Properties) -> String {\n        properties.common.path\n    }","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/file_sink/azblob.rs#L95-L131","documentation":"RisingWave's azblob file sink builds an AzblobConfig by converting the user's WITH options BTreeMap into a JSON value and deserializing it with serde_json::from_value. When any option is missing, has an unexpected type, or is not a recognized field, serde fails and the error is wrapped in SinkError::Config with the message \"config deserialization error: {e}\". It means the CREATE SINK options could not be parsed into the connector's typed config struct.","triggerScenarios":"CREATE SINK with connector='azblob' where a required field (e.g. azblob_container_name, azblob_account_name, azblob_access_key, path, r#type) is absent, or a field is given a wrongly-typed value, or an unknown/misspelled option name is supplied so serde deserialization of BTreeMap -> AzblobConfig fails.","commonSituations":"Typo in a WITH option key (e.g. 'container' instead of 'azblob_container_name'); forgetting the access key or account name; copying options from another sink connector (s3/gcs) whose field names differ; newer RisingWave versions adding required fields that older create statements lack.","solutions":["Check the error's inner serde message: it names the exact missing/unknown/invalid field; fix that WITH option in CREATE SINK.","Verify required azblob options are present: azblob_container_name, azblob_account_name, azblob_access_key, plus r#type, path, and format options.","Correct any misspelled or unsupported option names and remove options not defined on AzblobConfig.","Confirm credentials are plain string values with no characters that break option parsing.","Consult the azblob sink documentation for the current option list, since fields can change between versions."],"exampleFix":"// before\nCREATE SINK s FROM mv WITH (\n  connector = 'azblob',\n  container_name = 'mycontainer',\n  type = 'append-only'\n);\n// after (correct option names + required fields)\nCREATE SINK s FROM mv WITH (\n  connector = 'azblob',\n  azblob_container_name = 'mycontainer',\n  azblob_account_name = 'myaccount',\n  azblob_access_key = 'secret-key',\n  r#type = 'append-only',\n  path = 'exports/'\n);","handlingStrategy":"validation","validationCode":"const REQUIRED: [&str; 4] = [\"azblob_container_name\", \"azblob_account_name\", \"azblob_access_key\", \"path\"];\nfor k in REQUIRED {\n    if !opts.contains_key(k) {\n        return Err(format!(\"missing azblob sink option: {k}\"));\n    }\n}\nmatch opts.get(\"r#type\").map(String::as_str) {\n    Some(\"append-only\") | Some(\"upsert\") => {}\n    other => return Err(format!(\"r#type must be append-only or upsert, got {other:?}\")),\n}","typeGuard":null,"tryCatchPattern":"match AzblobSink::from_btreemap(opts) {\n    Ok(cfg) => proceed(cfg),\n    Err(SinkError::Config(e)) => eprintln!(\"fix sink WITH options: {e:#}\"),\n    Err(e) => return Err(e),\n}","preventionTips":["Validate WITH options against the connector schema before running CREATE SINK.","Keep a canonical example DDL per connector and copy option names from it.","Match option names exactly to AzblobConfig fields; avoid aliases from other connectors.","Always set r#type explicitly on every file sink.","After upgrading RisingWave, re-check sink DDL against the new config fields."],"tags":["sink","config","serialization","azblob"],"backgroundTag":"missing-required-config-field","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}