{"record":{"id":"37c66209f1f8098c","repo":"risingwavelabs/risingwave","slug":"invalid-order-key-token-token-in-item-hint","errorCode":null,"errorMessage":"Invalid order key token `{token}` in `{item}`\nHINT: Supported format is `column [asc|desc] [nulls first|last]`","messagePattern":"Invalid order key token `(.+?)` in `(.+?)`\nHINT: Supported format is `column \\[asc\\|desc\\] \\[nulls first\\|last\\]`","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/connector/src/sink/iceberg/create_table.rs","lineNumber":539,"sourceCode":"                    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            }\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","sourceCodeStart":521,"sourceCodeEnd":557,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/sink/iceberg/create_table.rs#L521-L557","documentation":"Thrown by `parse_order_key_exprs` as the catch-all branch when a token in an order key item matches none of the recognized keywords (`asc`, `desc`, `nulls`). The grammar for each item is `column [asc|desc] [nulls first|last]`, so any unexpected extra or misspelled token is rejected with a HINT showing the supported format.","triggerScenarios":"Passing items with unsupported syntax such as `col asc collate x`, `col asc nulls`, direction typos like `col asec`, extra separators like `col!`, or multi-word garbage in the Iceberg sink `order_key` option.","commonSituations":"Copying PostgreSQL-style ORDER BY clauses with constructs (COLLATE, arithmetic, functions) unsupported by the Iceberg sort-order mini-grammar; typos in `asc`/`desc`; stray commas or whitespace inside a quoted item.","solutions":["Rewrite the item to `column [asc|desc] [nulls first|last]`, e.g. `ts desc nulls last`","Remove unsupported tokens (COLLATE, expressions, functions) — only plain column names are allowed","Split multiple columns into a comma-separated list of items, each following the supported format"],"exampleFix":"// before\norder_key = 'lower(name) asc, ts asec'\n// after\norder_key = 'name asc, ts desc nulls last'","handlingStrategy":"validation","validationCode":"fn item_matches_grammar(item: &str) -> bool {\n    let toks: Vec<&str> = item.split_whitespace().collect();\n    let dir_ok = |t: Option<&&str>| t.map_or(true, |d| matches!(*d, \"asc\" | \"desc\" | \"ASC\" | \"DESC\"));\n    let is_ident = |t: &str| !t.is_empty() && t.chars().all(|c| c.is_alphanumeric() || c == '_');\n    match toks.as_slice() {\n        [c] if is_ident(c) => true,\n        [c, d] if is_ident(c) && dir_ok(Some(&d)) => true,\n        [c, d, \"nulls\", o] if is_ident(c) && dir_ok(Some(&d)) && matches!(*o, \"first\" | \"last\" | \"FIRST\" | \"LAST\") => true,\n        _ => false,\n    }\n}\nassert!(order_key.split(',').all(|i| item_matches_grammar(i.trim())));","typeGuard":null,"tryCatchPattern":"match parse_order_key_exprs(item) {\n    Ok(keys) => keys,\n    Err(e) if e.to_string().contains(\"Supported format is\") => {\n        eprintln!(\"normalize item to `column [asc|desc] [nulls first|last]`: {e}\");\n        Default::default()\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Read the HINT in the message: only `column [asc|desc] [nulls first|last]` is supported","Strip SQL-only clauses (COLLATE, expressions) before passing order keys","Run a dry parse of order_key strings in config tests"],"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"}