{"record":{"id":"20d47201ab041545","repo":"risingwavelabs/risingwave","slug":"config-deserialization-error-e-20d472","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/gcs.rs","lineNumber":98,"sourceCode":"\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 GcsSink;\n\nimpl OpendalSinkBackend for GcsSink {\n    type Properties = GcsConfig;\n\n    const SINK_NAME: &'static str = GCS_SINK;\n\n    fn from_btreemap(btree_map: BTreeMap<String, String>) -> Result<Self::Properties> {\n        let config = serde_json::from_value::<GcsConfig>(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: GcsConfig) -> Result<Operator> {\n        FileSink::<GcsSink>::new_gcs_sink(properties)\n    }\n\n    fn get_path(properties: Self::Properties) -> String {\n        properties.common.path\n    }","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/file_sink/gcs.rs#L80-L116","documentation":"The GCS file sink derives GcsConfig from the WITH options BTreeMap by serializing it to JSON and deserializing into the typed struct. A missing required field, wrong value type, or unrecognized key makes serde_json::from_value fail, surfaced as SinkError::Config with \"config deserialization error: {e}\". The wrapped serde message pinpoints the field that could not be converted.","triggerScenarios":"CREATE SINK with connector='gcs' missing required options (gcs_bucket_name, gcs_service_account_key, path, r#type, file_format), supplying a mistyped value, or passing an option key that does not exist on GcsConfig.","commonSituations":"Forgetting gcs_service_account_key or providing an invalid service-account JSON as the value; typo like 'bucket' instead of 'gcs_bucket_name'; copying s3/azblob options into a gcs sink; upgrades introducing new required fields.","solutions":["Inspect the inner serde error for the exact offending field and fix that WITH option.","Provide all required gcs options: gcs_bucket_name, gcs_service_account_key, r#type, path, and format options.","Make sure the service account key is a complete, valid JSON credential string.","Remove misspelled or connector-incompatible option names (s3_*/azblob_* fields are not valid for gcs).","Cross-check the gcs sink docs for the current option schema in your RisingWave version."],"exampleFix":"// before\nCREATE SINK s FROM mv WITH (\n  connector = 'gcs',\n  bucket_name = 'mybucket',\n  type = 'append-only'\n);\n// after\nCREATE SINK s FROM mv WITH (\n  connector = 'gcs',\n  gcs_bucket_name = 'mybucket',\n  gcs_service_account_key = '<full service account json>',\n  r#type = 'append-only',\n  path = 'exports/'\n);","handlingStrategy":"validation","validationCode":"const REQUIRED: [&str; 3] = [\"gcs_bucket_name\", \"gcs_service_account_key\", \"path\"];\nfor k in REQUIRED {\n    if !opts.contains_key(k) {\n        return Err(format!(\"missing gcs sink option: {k}\"));\n    }\n}\nlet key = &opts[\"gcs_service_account_key\"];\nif !key.trim_start().starts_with('{') {\n    return Err(\"gcs_service_account_key must be a JSON service account object\".to_string());\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 GcsSink::from_btreemap(opts) {\n    Ok(cfg) => proceed(cfg),\n    Err(SinkError::Config(e)) => eprintln!(\"fix gcs sink WITH options: {e:#}\"),\n    Err(e) => return Err(e),\n}","preventionTips":["Validate WITH options against the gcs connector schema before CREATE SINK.","Use gcs_-prefixed option names exactly as defined on GcsConfig.","Supply a complete service-account JSON for gcs_service_account_key.","Always set r#type explicitly.","After upgrades, re-check sink DDL against the current GcsConfig fields."],"tags":["sink","config","serialization","gcs"],"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"}