apache/iceberg · error · IllegalArgumentException
The Avro schema is not a nullable type: ${schema}
Error message
The Avro schema is not a nullable type: ${schema} What it means
RowDataToAvroConverters wraps each converter to unwrap nullable Avro fields before converting RowData values to Avro. When the writer's Avro schema for a field is a UNION, it must be exactly a 2-branch union with one NULL branch (the standard Avro nullable pattern). Any other union shape (3+ branches, or two non-null branches) cannot be unwrapped, so an IllegalArgumentException naming the offending schema is thrown.
Source
Thrown at flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/formats/avro/RowDataToAvroConverters.java:292
private static final long serialVersionUID = 1L;
@Override
public Object convert(Schema schema, Object object) {
if (object == null) {
return null;
}
// get actual schema if it is a nullable schema
Schema actualSchema;
if (schema.getType() == Schema.Type.UNION) {
List<Schema> types = schema.getTypes();
int size = types.size();
if (size == 2 && types.get(1).getType() == Schema.Type.NULL) {
actualSchema = types.get(0);
} else if (size == 2 && types.get(0).getType() == Schema.Type.NULL) {
actualSchema = types.get(1);
} else {
throw new IllegalArgumentException(
"The Avro schema is not a nullable type: " + schema.toString());
}
} else {
actualSchema = schema;
}
return converter.convert(actualSchema, object);
}
};
}
private static RowDataToAvroConverter createRowConverter(
RowType rowType, boolean legacyTimestampMapping) {
final RowDataToAvroConverter[] fieldConverters =
rowType.getChildren().stream()
.map(legacyType -> createConverter(legacyType, legacyTimestampMapping))
.toArray(RowDataToAvroConverter[]::new);
final LogicalType[] fieldTypes =
rowType.getFields().stream().map(RowType.RowField::getType).toArray(LogicalType[]::new);View on GitHub (pinned to 86d9c8fc54)
Solutions
- Inspect the field's Avro schema and reduce the union to exactly [T, null] or [null, T].
- If multiple non-null types are needed, promote the field to a single wider type (e.g. use double instead of [int, double]).
- Convert the value before writing so the union branch is resolved upstream, then pass the concrete (non-union) schema.
- If the union is genuinely 2-branch nullable, verify branch order/content — e.g. a nested union like [[int, null], null] is still rejected.
Example fix
// before
Schema fieldSchema = schema.getField("f").schema(); // ["int", "long", "null"]
converter.convert(fieldSchema, value); // throws
// after
Schema fieldSchema = Schema.createUnion(Schema.create(SchemaType.INT), Schema.create(SchemaType.NULL));
converter.convert(fieldSchema, value); // OK Defensive patterns
Strategy: validation
Validate before calling
static boolean isNullableUnion(Schema s) {
return s.getType() != Schema.Type.UNION
|| (s.getTypes().size() == 2
&& (s.getTypes().get(0).getType() == Schema.Type.NULL
|| s.getTypes().get(1).getType() == Schema.Type.NULL));
}
if (!isNullableUnion(fieldSchema)) throw new IllegalStateException("Bad union: " + fieldSchema); Type guard
if (schema.getType() == Schema.Type.UNION
&& schema.getTypes().size() == 2
&& schema.getTypes().stream().anyMatch(t -> t.getType() == Schema.Type.NULL)) {
// safe to convert
} Try / catch
try {
converter.convert(fieldSchema, value);
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("The Avro schema is not a nullable type")) {
// log schema, use resolved/concrete branch schema instead
} else throw e;
} Prevention
- Only emit [T, null] or [null, T] unions in schemas feeding Flink Avro writers.
- Validate all Avro schemas with a checker that rejects multi-branch unions at startup.
- Avoid schema evolution that widens a field's union; widen the base type instead.
When it happens
Trigger: Calling RowDataToAvroConverters.createConverter / converter.convert(schema, object) where schema.getType() == UNION but the union does not have exactly 2 branches with one being Schema.Type.NULL — e.g. union [int, long, null], [string, bytes], or [null] alone.
Common situations: Feeding a generic Avro schema produced outside Iceberg/Flink that uses multi-branch unions; schema evolution merging multiple types into one field; hand-written Avro schemas with unions like ["null","int","long"] passed to Flink's Avro writer.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Unsupported type:
- Unsupported logical type: ${logicalType}
- Fail to serialize at field: %s.
- Unsupported Avro type '${schema.getType()}'.
- The Avro schema is not a nullable type: ${schema.toString()}
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/12ff54be34d5cee2.
Report an issue: GitHub.