{"record":{"id":"8c42ac71857cd1a8","repo":"risingwavelabs/risingwave","slug":"e-8c42ac","errorCode":null,"errorMessage":"{e}","messagePattern":"\\{e\\}","errorType":"error_code","errorClass":"SinkError::Config","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/http.rs","lineNumber":77,"sourceCode":"impl EnforceSecret for HttpConfig {}\n\nimpl HttpConfig {\n    pub fn from_btreemap(\n        values: BTreeMap<String, String>,\n    ) -> Result<(Self, BTreeMap<String, String>)> {\n        // Extract header.* keys before serde parsing\n        let mut headers = BTreeMap::new();\n        let mut rest = BTreeMap::new();\n        for (k, v) in &values {\n            if let Some(header_name) = k.strip_prefix(\"header.\") {\n                headers.insert(header_name.to_owned(), v.clone());\n            } else {\n                rest.insert(k.clone(), v.clone());\n            }\n        }\n\n        let config = serde_json::from_value::<HttpConfig>(serde_json::to_value(rest).unwrap())\n            .map_err(|e| SinkError::Config(anyhow!(e)))?;\n\n        if config.r#type != SINK_TYPE_APPEND_ONLY {\n            return Err(SinkError::Config(anyhow!(\n                \"HTTP sink only supports append-only mode\"\n            )));\n        }\n\n        Ok((config, headers))\n    }\n}\n\n#[derive(Clone, Debug)]\nenum HttpUrl {\n    Static(reqwest::Url),\n    Dynamic { url_index: usize },\n}\n\n/// Validates the HTTP sink parameters and returns the sink so callers can use it directly without","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/http.rs#L59-L95","documentation":"This wraps a serde deserialization failure when converting the remaining sink options into an `HttpConfig` via `serde_json::from_value`. A provided option has a wrong name or type for HttpConfig, so deserialization fails.","triggerScenarios":"Calling `from_btreemap` with user-provided sink options (with known keys like url/method/headers removed into `rest`) that contain an unknown field or wrong-typed value for HttpConfig.","commonSituations":"Typo in a sink option key (e.g. 'conector' instead of 'connector'); passing a string where a number/bool is expected; extra legacy options not recognized by HttpConfig's serde definition.","solutions":["Check each WITH option against HttpConfig's supported fields and fix typos","Ensure value types match the struct fields (string vs bool vs number)","Remove unsupported/legacy options from the WITH clause","Enable serde deny_unknown_fields awareness — consult docs for accepted options"],"exampleFix":"// before\nCREATE SINK s FROM mv WITH (connector='http', url='...', method='POST', unknowOpt='x');\n// after\nCREATE SINK s FROM mv WITH (connector='http', url='...', method='POST');","handlingStrategy":"validation","validationCode":"// lint your WITH options against HttpConfig fields before creating the sink\nlet allowed = [\"connector\", \"url\", \"method\", \"type\", \"headers\"];\nlet unknown: Vec<_> = options.keys().filter(|k| !allowed.contains(&k.as_str())).collect();\nif !unknown.is_empty() { return Err(format!(\"unknown http sink options: {unknown:?}\")); }","typeGuard":null,"tryCatchPattern":"match HttpSink::from_btreemap(&props, ...) {\n    Err(SinkError::Config(e)) => log::error!(\"bad HTTP sink config: {e:#}\"),\n    ok => ok?,\n}","preventionTips":["Cross-check every WITH option against documented HttpConfig fields","Fix typos in option keys","Keep value types exactly as documented (strings quoted, booleans unquoted)"],"tags":["http","config","serde","sink","options"],"backgroundTag":"invalid-config-value","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"}