risingwavelabs/risingwave · error · SinkError::Config
`{}` must be {}, or {}
Error message
`{}` must be {}, or {} What it means
After deserializing, `from_btreemap` checks that the `type` option equals `append-only` or `upsert`. Any other value raises this `SinkError::Config`, telling the user which values are accepted (`{}` placeholders expand to the option name 'type', 'append-only', 'upsert'). StarRocks sink semantics differ fundamentally between the two modes, so no other value is meaningful.
Source
Thrown at src/connector/src/sink/starrocks.rs:161
crate::impl_sink_unknown_fields!(StarrocksConfig);
impl EnforceSecret for StarrocksConfig {
fn enforce_one(prop: &str) -> crate::error::ConnectorResult<()> {
StarrocksCommon::enforce_one(prop)
}
}
fn default_commit_checkpoint_interval() -> u64 {
DEFAULT_COMMIT_CHECKPOINT_INTERVAL_WITH_SINK_DECOUPLE
}
impl StarrocksConfig {
pub fn from_btreemap(properties: BTreeMap<String, String>) -> Result<Self> {
let config =
serde_json::from_value::<StarrocksConfig>(serde_json::to_value(properties).unwrap())
.map_err(|e| SinkError::Config(anyhow!(e)))?;
if config.r#type != SINK_TYPE_APPEND_ONLY && config.r#type != SINK_TYPE_UPSERT {
return Err(SinkError::Config(anyhow!(
"`{}` must be {}, or {}",
SINK_TYPE_OPTION,
SINK_TYPE_APPEND_ONLY,
SINK_TYPE_UPSERT
)));
}
if config.commit_checkpoint_interval == 0 {
return Err(SinkError::Config(anyhow!(
"`commit_checkpoint_interval` must be greater than 0"
)));
}
if let Some(0) = config.max_batch_size_bytes {
return Err(SinkError::Config(anyhow!(
"`starrocks.max_batch_size_bytes` must be greater than 0"
)));
}
Ok(config)
}View on GitHub (pinned to 6469eb736d)
Solutions
- Set `type='append-only'` or `type='upsert'` (exact lowercase strings) in the WITH options.
- Use 'upsert' if the target StarRocks table has a key model and you need updates/deletes; 'append-only' for pure inserts.
- Check other connector examples you copied — the `type` vocabulary is sink-specific in RisingWave.
Example fix
-- before CREATE SINK s FROM mv WITH (connector='starrocks', type='append', ...); -- after CREATE SINK s FROM mv WITH (connector='starrocks', type='append-only', ...);
Defensive patterns
Strategy: validation
Validate before calling
// Rust: check the type option before constructing the sink
fn valid_sink_type(props: &BTreeMap<String, String>) -> bool {
matches!(props.get("type").map(String::as_str), Some("append-only") | Some("upsert"))
} Type guard
fn is_valid_sink_type(v: &str) -> bool { v == "append-only" || v == "upsert" } Try / catch
match StarrocksConfig::from_btreemap(props) {
Err(e) if e.to_string().contains("must be append-only, or upsert") => {
eprintln!("set type='append-only' or type='upsert'");
}
other => other,
} Prevention
- Use the exact lowercase strings 'append-only' or 'upsert'
- Don't reuse `type` values from other connectors
- Validate user input in dashboards/tools that build CREATE SINK statements
When it happens
Trigger: Creating a StarRocks sink with `type='insert'`, `type='append'`, `type='AppendOnly'` (wrong case), or any string other than the two accepted constants `SINK_TYPE_APPEND_ONLY` / `SINK_TYPE_UPSERT`.
Common situations: Copy-pasting connector examples from other sinks (e.g. Kafka's 'append'); case-sensitivity mistakes; assuming 'upsert' is spelled 'Upsert' or 'debezium'.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- sink format unsupported: {}
- sink encode unsupported: {}
- unsupported {} as sink key encode
- `{}` must be {}, or {}
- {e}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/394a674c5de5f83d.
Report an issue: GitHub.