{"record":{"id":"f35128d546b5a2b6","repo":"risingwavelabs/risingwave","slug":"must-be-or-f35128","errorCode":null,"errorMessage":"`{}` must be {}, or {}","messagePattern":"`(.+?)` must be (.+?), or (.+?)","errorType":"validation","errorClass":"SinkError","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/file_sink/webhdfs.rs","lineNumber":89,"sourceCode":"impl UnknownFields for WebhdfsConfig {\n    fn unknown_fields(&self) -> HashMap<String, String> {\n        self.unknown_fields.clone()\n    }\n}\n\ncrate::impl_sink_unknown_fields!(WebhdfsConfig);\n\nimpl OpendalSinkBackend for WebhdfsSink {\n    type Properties = WebhdfsConfig;\n\n    const SINK_NAME: &'static str = WEBHDFS_SINK;\n\n    fn from_btreemap(btree_map: BTreeMap<String, String>) -> Result<Self::Properties> {\n        let config =\n            serde_json::from_value::<WebhdfsConfig>(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: WebhdfsConfig) -> Result<Operator> {\n        FileSink::<WebhdfsSink>::new_webhdfs_sink(properties)\n    }\n\n    fn get_path(properties: Self::Properties) -> String {\n        properties.common.path\n    }\n\n    fn get_engine_type() -> super::opendal_sink::EngineType {","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/file_sink/webhdfs.rs#L71-L107","documentation":"WebHDFS file sink config validation in from_btreemap: the `type` property (r#type) held a value other than the two supported sink types (append-only / deb-mutating, per the `{}` must be {}, or {} template). Same strict enum guard as the S3 backend, applied after serde-parsing WebhdfsConfig.","triggerScenarios":"`CREATE SINK ... WITH (connector='webhdfs', type='...')` with a value other than the two allowed literals (typos, wrong case, underscores).","commonSituations":"Underscore vs hyphen mistakes ('append_only'), capitalization, or reusing `type` values from unrelated systems.","solutions":["Use `type='append-only'` or `type='upsert'` verbatim","Check spelling and separator characters","Choose based on data characteristics: upsert only if the stream contains update/delete operations"],"exampleFix":"// before\nWITH (connector='webhdfs', type='upsert-only', ...)\n// after\nWITH (connector='webhdfs', type='upsert', ...)","handlingStrategy":"validation","validationCode":"const allowed = ['append-only', 'upsert'];\nif (opts.type !== undefined && !allowed.includes(opts.type)) {\n  throw new Error(`webhdfs sink type must be ${allowed.join(' or ')}`);\n}","typeGuard":"const isValidWebhdfsSinkType = (t) => t === 'append-only' || t === 'upsert';","tryCatchPattern":"try { createSink(opts); } catch (e) { if (String(e).includes('must be append-only, or upsert')) { opts.type = 'append-only'; createSink(opts); } else { throw e; } }","preventionTips":["Use exact hyphenated lowercase values","Normalize case/underscores before submitting DDL","Base the choice on stream semantics (append vs upsert)"],"tags":["config","webhdfs","sink","validation"],"backgroundTag":"invalid-enum-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"}