risingwavelabs/risingwave · error · SinkError
`{}` must be {}, or {}
Error message
`{}` must be {}, or {} What it means
This error is thrown when creating a GCS file sink whose `type` property is neither 'append-only' nor 'upsert'. The sink connector validates the user-provided `sink.type` option against the two supported sink types and rejects anything else with a SinkError::Config. It is a fail-fast validation in `from_btreemap` before the sink is ever instantiated.
Source
Thrown at src/connector/src/sink/file_sink/gcs.rs:100
.layer(LoggingLayer::default())
.layer(RetryLayer::default());
Ok(operator)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GcsSink;
impl OpendalSinkBackend for GcsSink {
type Properties = GcsConfig;
const SINK_NAME: &'static str = GCS_SINK;
fn from_btreemap(btree_map: BTreeMap<String, String>) -> Result<Self::Properties> {
let config = serde_json::from_value::<GcsConfig>(serde_json::to_value(btree_map).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
)));
}
Ok(config)
}
fn new_operator(properties: GcsConfig) -> Result<Operator> {
FileSink::<GcsSink>::new_gcs_sink(properties)
}
fn get_path(properties: Self::Properties) -> String {
properties.common.path
}
fn get_engine_type() -> super::opendal_sink::EngineType {View on GitHub (pinned to 6469eb736d)
Solutions
- Set the WITH option to a supported value: `type = 'append-only'` or `type = 'upsert'`
- Check for typos in the value (exact strings 'append-only' and 'upsert')
- If upsert semantics are needed with a file sink, note file sinks require append-only output, so prefer `type = 'append-only'` combined with `force_append_only = 'true'` on the encode
Example fix
// before WITH ( connector = 'gcs', type = 'append' ) // after WITH ( connector = 'gcs', type = 'append-only' )
Defensive patterns
Strategy: validation
Validate before calling
let supported = ["append-only", "upsert"];
let sink_type = props.get("type").map(|s| s.as_str()).unwrap_or("");
if !supported.contains(&sink_type) {
return Err(format!("GCS sink requires type to be one of {:?}, got '{}'", supported, sink_type));
} Type guard
fn is_valid_sink_type(v: &str) -> bool {
matches!(v, "append-only" | "upsert")
} Prevention
- Always specify `type` explicitly in the WITH clause for GCS file sinks
- Keep sink definitions in version-controlled SQL files with supported values only
- Check the connector's supported sink types in docs before copying configs from other connectors
When it happens
Trigger: Creating a GCS sink where the WITH option `type` is set to an unsupported value (e.g. `type = 'debezium'`, `type = 'append'`, misspelled 'append-only'), or where `type` deserializes into a GcsConfig with an unexpected string.
Common situations: Typo in the sink type option; copying config from a non-file sink connector that uses other type values; assuming the option name is something other than `type` and a stale/empty value is parsed.
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
- `commit_checkpoint_interval` must be greater than 0
- `{}` must be {}, or {}
- `{}` must be {}, or {}
- config deserialization error: {e}
- `{}` must be {}, or {}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/86770e88d7b0cda6.
Report an issue: GitHub.