risingwavelabs/risingwave · error
Column order mismatch at position {}: the sink has column `{
Error message
Column order mismatch at position {}: the sink has column `{}` but the Iceberg table has column `{}`. The Iceberg sink maps columns to the table by position, so the sink's column order must match the Iceberg table columns [{}]. What it means
The Iceberg sink writes columns to the table by position, so `try_matches_arrow_schema` zips RW and Iceberg fields and requires names to match at each index. If positionally aligned columns have different names, it bails with a detailed message listing the position, both column names, and the full Iceberg column list.
Source
Thrown at src/connector/src/sink/iceberg/create_table.rs:424
let mut schema_fields = HashMap::new();
rw_schema.fields.iter().for_each(|field| {
let res = schema_fields.insert(field.name.as_str(), &field.data_type);
// This assert is to make sure there is no duplicate field name in the schema.
assert!(res.is_none())
});
check_compatibility(schema_fields, &arrow_schema.fields)?;
// The sink writes columns to the Iceberg table by position, so the column order
// must match. The check above only validates the name set and types.
for (idx, (rw_field, arrow_field)) in rw_schema
.fields
.iter()
.zip_eq_fast(arrow_schema.fields().iter())
.enumerate()
{
if rw_field.name.as_str() != arrow_field.name().as_str() {
bail!(
"Column order mismatch at position {}: the sink has column `{}` but the \
Iceberg table has column `{}`. The Iceberg sink maps columns to the table \
by position, so the sink's column order must match the Iceberg table \
columns [{}].",
idx,
rw_field.name,
arrow_field.name(),
arrow_schema.fields().iter().map(|f| f.name()).join(", "),
);
}
}
Ok(())
}
pub fn parse_partition_by_exprs(
expr: String,
) -> std::result::Result<Vec<(String, Transform)>, anyhow::Error> {View on GitHub (pinned to 6469eb736d)
Solutions
- Reorder the sink query's SELECT list to exactly match the Iceberg table's column order.
- Recreate the sink (or the table) so both schemas are generated in the same order.
- Check the full printed Iceberg column list in the message and align one-to-one positionally.
Example fix
// before: table is (a, b), sink selects (b, a) CREATE SINK s AS SELECT b, a FROM t; // after CREATE SINK s AS SELECT a, b FROM t;
Defensive patterns
Strategy: validation
Validate before calling
// verify positional names match before creating
for (i, (rw, arrow)) in rw_schema.fields.iter().zip(arrow_schema.fields()).enumerate() {
if rw.name != arrow.name() {
eprintln!("position {i}: sink `{}` vs table `{}`", rw.name, arrow.name());
}
} Prevention
- Order the sink SELECT list to mirror the Iceberg table's column order exactly.
- Avoid inserting columns into existing Iceberg tables that sinks write to; add columns at the end and recreate the sink.
- Treat the Iceberg table DDL as the source of truth for the sink projection.
When it happens
Trigger: Called from `create_and_validate_table_impl`: e.g., sink schema is (a, b) but Iceberg table is (b, a) — same set, wrong order.
Common situations: Sink query column order differs from the table's column order; Iceberg table was created from a different column order or evolved by inserting a column; renaming a column in the table externally.
Related errors
- Field {} not found in our schema
- field {}'s type is incompatible RisingWave converted data ty
- Schema length mismatch, risingwave is {}, and iceberg is {}
- {} unsupported in {} catalog
- Can't find schema by id {}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/73f93f65df4246f5.
Report an issue: GitHub.