apache/seatunnel · error · IllegalArgumentException
Cannot convert to struct:
Error message
Cannot convert to struct:
What it means
RowConverter.convertStructValue dispatches on the SeaTunnel SqlType of the value being converted into an Iceberg struct; only ROW (SeaTunnelRow) is convertible, anything else reaches default and throws IllegalArgumentException naming the SqlType.
Source
Thrown at seatunnel-connectors-v2/connector-iceberg/src/main/java/org/apache/seatunnel/connectors/seatunnel/iceberg/data/RowConverter.java:118
return convertStructValue(row, rowType, tableSchema.asStruct(), -1, wrapper);
}
protected GenericRecord convertStructValue(
Object value,
SeaTunnelDataType<?> fromType,
Types.StructType schema,
int parentFieldId,
SchemaChangeWrapper wrapper) {
switch (fromType.getSqlType()) {
case ROW:
return convertToStruct(
(SeaTunnelRow) value,
(SeaTunnelRowType) fromType,
schema,
parentFieldId,
wrapper);
default:
throw new IllegalArgumentException(
"Cannot convert to struct: " + fromType.getSqlType().name());
}
}
/** Convert RowType */
private GenericRecord convertToStruct(
SeaTunnelRow row,
SeaTunnelRowType fromType,
Types.StructType schema,
int structFieldId,
SchemaChangeWrapper wrapper) {
GenericRecord result = GenericRecord.create(schema);
String[] fieldNames = fromType.getFieldNames();
for (int i = 0; i < fieldNames.length; i++) {
String recordField = fieldNames[i];
Type afterType = SchemaUtils.toIcebergType(fromType.getFieldType(i));
Types.NestedField tableField = lookupStructField(recordField, schema, structFieldId);
// add columnView on GitHub (pinned to cf67b549a7)
Solutions
- Align the SeaTunnel source/transform schema so struct fields carry SeaTunnelRowType (ROW).
- Wrap non-row values into a SeaTunnelRow matching the struct schema before writing.
- Change the Iceberg table schema to a non-struct type matching the data.
- Catch IllegalArgumentException and log/reject the offending record.
Example fix
// before
row.setField(i, someString); // struct field
// after
row.setField(i, new SeaTunnelRow(new Object[]{...})); // SeaTunnelRowType for struct field Defensive patterns
Strategy: type-guard
Validate before calling
if (fromType.getSqlType() != SqlType.ROW) { throw new IllegalArgumentException("Expected ROW for struct field, got " + fromType.getSqlType()); } Type guard
boolean isRow(Object v, SeaTunnelType<?> t) { return t instanceof SeaTunnelRowType && v instanceof SeaTunnelRow; } Try / catch
try { record = converter.convert(row, fromType, schema); } catch (IllegalArgumentException e) { log.error("Struct conversion failed: {}", e.getMessage()); } Prevention
- Match SeaTunnelRowType shape to Iceberg struct schema
- Use transforms to reshape data into ROW before the sink
- Validate schemas at job startup
When it happens
Trigger: Calling convert()/convertValue() with a fromType whose SqlType is not ROW at a position where the target Iceberg type is a struct (e.g. mapping a map/array/string into a struct field).
Common situations: SeaTunnel-to-Iceberg schema mismatches where a nested Iceberg struct field is fed by a non-row SeaTunnel column; wrong sink schema config or upstream transform producing a different shape.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Unsupported convert ${value.getClass()} to Row, typeDefine:
- Expected Array type but got:
- READ_FAILED
- Cannot convert to int:
- Cannot convert to long:
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/c7d90029d561fbb3.
Report an issue: GitHub.