{"record":{"id":"dd939eb7d0ed687c","repo":"risingwavelabs/risingwave","slug":"invalid-order-key-item-item-nulls-must-be-f","errorCode":null,"errorMessage":"Invalid order key item `{item}`: `NULLS` must be followed by `FIRST` or `LAST`","messagePattern":"Invalid order key item `(.+?)`: `NULLS` must be followed by `FIRST` or `LAST`","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/iceberg/create_table.rs","lineNumber":525,"sourceCode":"            bail!(\"Duplicate column `{column}` in order_key\");\n        }\n\n        let mut direction = SortDirection::Ascending;\n        let mut null_order = None;\n        let mut idx = 1;\n        while idx < tokens.len() {\n            match tokens[idx].to_ascii_lowercase().as_str() {\n                \"asc\" => {\n                    direction = SortDirection::Ascending;\n                    idx += 1;\n                }\n                \"desc\" => {\n                    direction = SortDirection::Descending;\n                    idx += 1;\n                }\n                \"nulls\" => {\n                    let order = tokens.get(idx + 1).ok_or_else(|| {\n                        anyhow!(\n                            \"Invalid order key item `{item}`: `NULLS` must be followed by `FIRST` or `LAST`\"\n                        )\n                    })?;\n                    null_order = Some(match order.to_ascii_lowercase().as_str() {\n                        \"first\" => NullOrder::First,\n                        \"last\" => NullOrder::Last,\n                        _ => bail!(\n                            \"Invalid order key item `{item}`\\nHINT: `NULLS` must be followed by `FIRST` or `LAST`\"\n                        ),\n                    });\n                    idx += 2;\n                }\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            }","sourceCodeStart":507,"sourceCodeEnd":543,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/iceberg/create_table.rs#L507-L543","documentation":"This error is thrown by `parse_order_key_exprs` when an order key item contains the keyword `NULLS` but there is no following token (end of the item string). The parser expects `NULLS` to be immediately followed by `FIRST` or `LAST` to determine null ordering for the Iceberg sort order; a dangling `NULLS` is a syntax error.","triggerScenarios":"Calling the Iceberg sink `order_key` option with an item such as `col asc nulls` (trailing `nulls` with nothing after it) or `col nulls` with no null-order word; typically a typo or a truncated config value.","commonSituations":"Hand-editing connector options in a CREATE SINK statement or a TOML/CLI config and truncating the value; copy-pasting a partially written sort spec; forgetting `FIRST`/`LAST` after adding `NULLS`.","solutions":["Append `FIRST` or `LAST` after `NULLS` in the order key item, e.g. `col asc nulls first`","Remove the dangling `NULLS` keyword if default null ordering is acceptable","Verify the full order_key string follows `column [asc|desc] [nulls first|last]`"],"exampleFix":"// before\norder_key = 'ts asc nulls'\n// after\norder_key = 'ts asc nulls first'","handlingStrategy":"validation","validationCode":"fn is_valid_order_key(item: &str) -> bool {\n    let toks: Vec<&str> = item.split_whitespace().collect();\n    match toks.as_slice() {\n        [_] => true,\n        [_, d] => matches!(d.to_ascii_lowercase().as_str(), \"asc\" | \"desc\"),\n        [_, d, \"nulls\", o] if matches!(o.to_ascii_lowercase().as_str(), \"first\" | \"last\") => {\n            matches!(d.to_ascii_lowercase().as_str(), \"asc\" | \"desc\")\n        }\n        _ => false,\n    }\n}\nassert!(order_key.split(',').all(|i| is_valid_order_key(i.trim())));","typeGuard":null,"tryCatchPattern":"match parse_order_key_exprs(item) {\n    Ok(keys) => keys,\n    Err(e) if e.to_string().contains(\"NULLS\") => {\n        // fix or drop the NULLS clause, then retry\n        eprintln!(\"fix NULLS clause: {e}\");\n        Default::default()\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always write the full `nulls first`/`nulls last` phrase, never a bare `nulls`","Validate order_key strings against the documented grammar before submitting sink options","Prefer omitting the NULLS clause when default null ordering is fine"],"tags":["rust","parsing","iceberg","sink"],"backgroundTag":"invalid-argument-format","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"}