risingwavelabs/risingwave · error

Invalid order key: empty item in `{expr}`

Error message

Invalid order key: empty item in `{expr}`

What it means

parse_order_key_exprs splits the order_key option string on commas and parses each item. An empty item means a bare or doubled comma (or a trailing/leading comma) was found, so the parser cannot derive a sort column for that position. It is thrown to surface a malformed order_key specification early, before an Iceberg table is created with a bad sort spec.

Source

Thrown at src/connector/src/sink/iceberg/create_table.rs:487

                Transform::from_str(&func)
                    .with_context(|| format!("invalid transform function {}", func))?,
            )
        };
        partition_columns.push((column.to_owned(), transform));
    }
    Ok(partition_columns)
}

pub fn parse_order_key_exprs(
    expr: String,
) -> std::result::Result<Vec<IcebergOrderKeyField>, anyhow::Error> {
    let mut order_keys = Vec::new();
    let mut seen_columns = std::collections::HashSet::new();

    for raw_item in expr.split(',') {
        let item = raw_item.trim();
        if item.is_empty() {
            bail!("Invalid order key: empty item in `{expr}`");
        }

        let tokens = item.split_whitespace().collect_vec();
        if tokens.is_empty() {
            bail!("Invalid order key item `{item}`");
        }
        if tokens.len() > 4 {
            bail!(
                "Invalid order key item `{item}`\nHINT: Supported format is `column [asc|desc] [nulls first|last]`"
            );
        }

        let column = tokens[0];
        if !ORDER_KEY_COLUMN_RE.is_match(column) {
            bail!(
                "Invalid order key column `{column}`\nHINT: Only plain column names are supported in order_key"
            );
        }

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Remove the empty item: no leading, trailing, or doubled commas in order_key
  2. If a column variable is empty at runtime, fix the variable or drop the item before joining
  3. Print the raw expr (it is embedded in the message) and delete the offending comma
  4. If dynamic ordering is optional, pass no order_key rather than an empty string

Example fix

// before
WITH ( connector = 'iceberg', order_key = 'id, name,' )
// after
WITH ( connector = 'iceberg', order_key = 'id, name' )
Defensive patterns

Strategy: validation

Validate before calling

let expr = "id, name"; // user-supplied order_key
let items: Vec<&str> = expr.split(',').map(|s| s.trim()).collect();
if items.iter().any(|i| i.is_empty()) {
    return Err(format!("order_key `{expr}` has an empty item; remove doubled/trailing commas"));
}

Type guard

fn has_empty_order_key_item(expr: &str) -> bool {
    expr.split(',').any(|item| item.trim().is_empty())
}

Prevention

When it happens

Trigger: Calling validate_order_key_columns (via iceberg sink table creation) with an order_key string containing an empty element after comma splitting, e.g. `a,,b`, `a,`, `,a`, or order_key=`""`/`,`.

Common situations: Hand-written DDL with a trailing comma after the last column; programmatically joined column lists where a list element is empty; copy-pasted options with double commas; templated SQL where a variable evaluates to empty.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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