risingwavelabs/risingwave · error · SinkError::Config

invalid write_mode: {}, must be one of: {}, {}

Error message

invalid write_mode: {}, must be one of: {}, {}

What it means

This is the FromStr parser for the `IcebergWriteMode` sink option. It only accepts the exact strings defined by ICEBERG_WRITE_MODE_MERGE_ON_READ ("merge-on-read") and ICEBERG_WRITE_MODE_COPY_ON_WRITE ("copy-on-write"); any other value for the `write_mode` connector property fails sink validation with this config error. It is thrown at sink creation time before any data is written.

Source

Thrown at src/connector/src/sink/iceberg/config.rs:75

}

impl IcebergWriteMode {
    pub fn as_str(self) -> &'static str {
        match self {
            IcebergWriteMode::MergeOnRead => ICEBERG_WRITE_MODE_MERGE_ON_READ,
            IcebergWriteMode::CopyOnWrite => ICEBERG_WRITE_MODE_COPY_ON_WRITE,
        }
    }
}

impl std::str::FromStr for IcebergWriteMode {
    type Err = SinkError;

    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
        match s {
            ICEBERG_WRITE_MODE_MERGE_ON_READ => Ok(IcebergWriteMode::MergeOnRead),
            ICEBERG_WRITE_MODE_COPY_ON_WRITE => Ok(IcebergWriteMode::CopyOnWrite),
            _ => Err(SinkError::Config(anyhow!(format!(
                "invalid write_mode: {}, must be one of: {}, {}",
                s, ICEBERG_WRITE_MODE_MERGE_ON_READ, ICEBERG_WRITE_MODE_COPY_ON_WRITE
            )))),
        }
    }
}

impl TryFrom<&str> for IcebergWriteMode {
    type Error = <Self as std::str::FromStr>::Err;

    fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
        value.parse()
    }
}

impl TryFrom<String> for IcebergWriteMode {
    type Error = <Self as std::str::FromStr>::Err;

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Set write_mode to exactly 'merge-on-read' or 'copy-on-write' in the WITH clause
  2. Remove the write_mode option entirely to use the default value
  3. Check the connector documentation for the accepted literal strings (case-sensitive)
  4. Run SHOW CREATE SINK or the sink error message to confirm which invalid value was passed

Example fix

// before
WITH (connector = 'iceberg', write_mode = 'merge_on_read')
// after
WITH (connector = 'iceberg', write_mode = 'merge-on-read')
Defensive patterns

Strategy: validation

Validate before calling

const VALID_WRITE_MODES: [&str; 2] = ["merge-on-read", "copy-on-write"];
fn validate_write_mode(mode: &str) -> Result<(), String> {
    if VALID_WRITE_MODES.contains(&mode) {
        Ok(())
    } else {
        Err(format!(
            "invalid write_mode: {}, must be one of: merge-on-read, copy-on-write",
            mode
        ))
    }
}

Type guard

fn is_valid_write_mode(s: &str) -> bool {
    matches!(s, "merge-on-read" | "copy-on-write")
}

Try / catch

match s.parse::<IcebergWriteMode>() {
    Ok(mode) => mode,
    Err(e) => {
        // SinkError::Config: fix the WITH option string before retrying
        return Err(e.context("check write_mode in WITH clause: exact literal required"));
    }
}

Prevention

When it happens

Trigger: A CREATE SINK ... WITH (connector='iceberg', write_mode='...') statement supplies a write_mode string that is not exactly 'merge-on-read' or 'copy-on-write' (typo, wrong case, extra whitespace, or a value from another system such as 'mor' or 'upsert').

Common situations: Copy-pasting options from a different engine's iceberg connector docs; misspelling like 'merge_on_read' with underscores; using quotes/case variants like 'MergeOnRead'; renaming options during a version upgrade.

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


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/7762ecec60a411bd. Report an issue: GitHub.