{"record":{"id":"39292ba128d95687","repo":"risingwavelabs/risingwave","slug":"must-be-or-39292b","errorCode":null,"errorMessage":"`{}` must be {}, or {}","messagePattern":"`(.+?)` must be (.+?), or (.+?)","errorType":"validation","errorClass":"SinkError::Config","httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/sqlserver.rs","lineNumber":88,"sourceCode":"    pub r#type: String, // accept \"append-only\" or \"upsert\"\n\n    #[serde(flatten)]\n    pub unknown_fields: std::collections::HashMap<String, String>,\n}\n\ncrate::impl_sink_unknown_fields!(SqlServerConfig);\n\npub fn sql_server_default_schema() -> String {\n    \"dbo\".to_owned()\n}\n\nimpl SqlServerConfig {\n    pub fn from_btreemap(properties: BTreeMap<String, String>) -> Result<Self> {\n        let config =\n            serde_json::from_value::<SqlServerConfig>(serde_json::to_value(properties).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    pub fn full_object_path(&self) -> String {\n        format!(\"[{}].[{}].[{}]\", self.database, self.schema, self.table)\n    }\n}\n\nimpl EnforceSecret for SqlServerConfig {\n    const ENFORCE_SECRET_PROPERTIES: Set<&'static str> = phf_set! {\n        \"sqlserver.password\"\n    };","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/sqlserver.rs#L70-L106","documentation":"Required-option guard in SqlServerConfig::from_btreemap: an enumerated with-clause option (such as the sink `type`) was set to a value outside the allowed alternatives; the message lists the accepted values. Fires when the property passes serde but fails the explicit value check.","triggerScenarios":"Creating a SQL Server sink with WITH option `type` set to anything other than SINK_TYPE_APPEND_ONLY ('append_only') or SINK_TYPE_UPSERT ('upsert').","commonSituations":"Typo like 'append-only' or 'upsert_only', copying `type='debezium'` or other sink-type values from a different connector's config.","solutions":["Set the `type` option to exactly 'append_only' or 'upsert' in the sink WITH clause","Check for typos/extra whitespace/case mismatch in the value","Review the SQL Server sink documentation for the accepted sink types"],"exampleFix":"// before\nWITH ( 'type' = 'append-only' )\n// after\nWITH ( 'type' = 'append_only' )","handlingStrategy":"validation","validationCode":"fn check_sink_type(props: &BTreeMap<String, String>) -> Result<(), String> {\n    match props.get(\"type\").map(String::as_str) {\n        Some(\"append_only\") | Some(\"upsert\") => Ok(()),\n        Some(other) => Err(format!(\"type must be append_only or upsert, got {}\", other)),\n        None => Ok(()),\n    }\n}","typeGuard":"fn is_valid_sink_type(s: &str) -> bool { s == \"append_only\" || s == \"upsert\" }","tryCatchPattern":"match create_sink(cfg) {\n    Err(e) if e.to_string().contains(\"must be append_only, or upsert\") => fix_and_retry_with_valid_sink_type(),\n    other => other,\n}","preventionTips":["Always use the exact literals 'append_only' / 'upsert' (underscores)","Lint sink WITH clauses for known option values","Reuse shared constants rather than hand-typed strings"],"tags":["sqlserver","sink","config","enum"],"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"}