{"record":{"id":"474778db13765549","repo":"risingwavelabs/risingwave","slug":"order-key-must-not-be-empty","errorCode":null,"errorMessage":"order_key must not be empty","messagePattern":"order_key must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/iceberg/create_table.rs","lineNumber":555,"sourceCode":"                }\n                token => {\n                    bail!(\n                        \"Invalid order key token `{token}` in `{item}`\\nHINT: Supported format is `column [asc|desc] [nulls first|last]`\"\n                    );\n                }\n            }\n        }\n\n        order_keys.push(IcebergOrderKeyField {\n            column: column.to_owned(),\n            direction,\n            null_order: null_order\n                .unwrap_or_else(|| IcebergOrderKeyField::default_null_order(direction)),\n        });\n    }\n\n    if order_keys.is_empty() {\n        bail!(\"order_key must not be empty\");\n    }\n\n    Ok(order_keys)\n}\n\npub fn validate_order_key_columns<'a>(\n    order_key: &str,\n    columns: impl IntoIterator<Item = &'a str>,\n) -> std::result::Result<Vec<IcebergOrderKeyField>, anyhow::Error> {\n    let parsed = parse_order_key_exprs(order_key.to_owned())?;\n    let columns = columns\n        .into_iter()\n        .map(|column| column.to_ascii_lowercase())\n        .collect::<std::collections::HashSet<_>>();\n    for item in &parsed {\n        if item.column.starts_with('_') {\n            bail!(\n                \"System column `{}` is not allowed in order_key\",","sourceCodeStart":537,"sourceCodeEnd":573,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/iceberg/create_table.rs#L537-L573","documentation":"After parsing all comma-separated items, `parse_order_key_exprs` rejects an empty result: an Iceberg sort order requires at least one order key. An empty or whitespace-only `order_key` option yields no parsed keys and is treated as invalid rather than silently producing an unsorted table.","triggerScenarios":"Setting the Iceberg sink `order_key` option to an empty string (`''`), only commas (`',,,'`), or whitespace (`'   '`); also passing items that are all empty after trimming.","commonSituations":"Templated config where the order key variable is unset/empty; removing the only column from the key without unsetting the option; YAML/TOML keys left as empty values.","solutions":["Provide at least one column in order_key, e.g. `order_key = 'ts'`","Remove/unset the order_key option entirely if no sort order is desired (if the API permits)","Check templating/variable expansion so the key is not interpolated to an empty string"],"exampleFix":"// before\norder_key = ''\n// after\norder_key = 'event_time desc nulls last'","handlingStrategy":"validation","validationCode":"let trimmed = order_key.trim();\nif trimmed.is_empty() || trimmed.split(',').all(|i| i.trim().is_empty()) {\n    // unset the option or provide a real key\n}\nassert!(!order_key.trim().is_empty(), \"order_key must contain at least one column\");","typeGuard":null,"tryCatchPattern":"match parse_order_key_exprs(order_key) {\n    Ok(keys) if keys.is_empty() => eprintln!(\"order_key empty; provide at least one column\"),\n    Ok(keys) => keys,\n    Err(e) => return Err(e),\n}","preventionTips":["Check that templated/interpolated order_key values are non-empty before building sink options","Unset the order_key option rather than passing an empty string when no sort order is wanted","Add a config linter test rejecting empty/whitespace-only order_key"],"tags":["rust","validation","iceberg","sink"],"backgroundTag":"empty-required-field","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"}