{"record":{"id":"793afdbd070416b4","repo":"risingwavelabs/risingwave","slug":"config-deserialization-error-e-793afd","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/fs.rs","lineNumber":82,"sourceCode":"        let builder = Fs::default().root(&config.common.path);\n        let operator: Operator = Operator::new(builder)?\n            .layer(LoggingLayer::default())\n            .layer(RetryLayer::default());\n        Ok(operator)\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct FsSink;\n\nimpl OpendalSinkBackend for FsSink {\n    type Properties = FsConfig;\n\n    const SINK_NAME: &'static str = FS_SINK;\n\n    fn from_btreemap(btree_map: BTreeMap<String, String>) -> Result<Self::Properties> {\n        let config = serde_json::from_value::<FsConfig>(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: FsConfig) -> Result<Operator> {\n        FileSink::<FsSink>::new_fs_sink(properties)\n    }\n\n    fn get_path(properties: Self::Properties) -> String {\n        properties.common.path\n    }","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/file_sink/fs.rs#L64-L100","documentation":"The filesystem (fs) file sink parses its FsConfig from the WITH options BTreeMap via serde_json (to_value then from_value). If a required option is missing, wrongly typed, or an unrecognized key is present, serde fails and the connector returns SinkError::Config with \"config deserialization error: {e}\". The inner serde error identifies the offending field.","triggerScenarios":"CREATE SINK with connector='fs' where required fields such as r#type, path, or format/encode options are missing or malformed, an unknown/misspelled WITH key is passed, or a value cannot deserialize into the expected FsConfig field type.","commonSituations":"Omitting or misspelling the 'path' option; reusing s3/gcs option names with connector='fs'; mismatched format/encode combinations (e.g. format='plain' requires an encode); version drift where required fields changed.","solutions":["Read the serde message embedded in the error — it names the failing field — and fix that option.","Ensure mandatory fs options are present: path, r#type, and the file format/encode options for your data.","Remove or correct option names not defined on FsConfig.","Check format and encode combinations are valid (e.g. format='plain' with encode='json' or 'text').","Re-check the fs sink docs for the current expected option set after upgrading RisingWave."],"exampleFix":"// before\nCREATE SINK s FROM mv WITH (\n  connector = 'fs',\n  location = '/data/out',\n  type = 'append-only'\n);\n// after\nCREATE SINK s FROM mv WITH (\n  connector = 'fs',\n  path = '/data/out',\n  r#type = 'append-only',\n  format = 'plain',\n  encode = 'json'\n);","handlingStrategy":"validation","validationCode":"const REQUIRED: [&str; 2] = [\"path\", \"r#type\"];\nfor k in REQUIRED {\n    if !opts.contains_key(k) {\n        return Err(format!(\"missing fs sink option: {k}\"));\n    }\n}\nif let Some(fmt) = opts.get(\"format\") {\n    if fmt == \"plain\" && !opts.contains_key(\"encode\") {\n        return Err(\"format 'plain' requires an 'encode' option\".to_string());\n    }\n}","typeGuard":null,"tryCatchPattern":"match FsSink::from_btreemap(opts) {\n    Ok(cfg) => proceed(cfg),\n    Err(SinkError::Config(e)) => eprintln!(\"fix fs sink WITH options: {e:#}\"),\n    Err(e) => return Err(e),\n}","preventionTips":["Validate WITH options against the fs connector schema before CREATE SINK.","Use 'path' (not 'location' or 'dir') as the option key.","Verify format/encode combinations against docs for the chosen file format.","Do not mix in s3/gcs/azblob option names.","After version upgrades, re-run config validation for existing sink DDL."],"tags":["sink","config","serialization","filesystem"],"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-14T16:17:12.679Z"}