apache/iceberg · error · java.lang.IllegalArgumentException

The Avro schema is not a nullable type: ${schema.toString()}

Error message

The Avro schema is not a nullable type: ${schema.toString()}

What it means

During RowDataToAvroConverters.nullable conversion, if the target Avro schema is a union, the code expects exactly two branches with one being NULL (a nullable type). Any other union shape (3+ branches, or two non-null branches) cannot be resolved to a single actual schema and throws this IllegalArgumentException.

Source

Thrown at flink/v2.1/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

  1. Restructure the Avro schema so nullable fields are unions of exactly one type plus null
  2. Wrap non-null multi-branch unions into a single record/managed representation before serialization
  3. Flatten or split the union field into separate columns in the source table
  4. Validate the Avro schema's unions before wiring it as a sink format

Example fix

// before
{"type":["null","int","string"]}
// after
{"type":["null","string"]}
Defensive patterns

Strategy: validation

Validate before calling

Schema fieldSchema = schema.getField("f").schema();
if (fieldSchema.getType() == Schema.Type.UNION) {
  List<Schema> branches = fieldSchema.getTypes();
  long nulls = branches.stream().filter(s -> s.getType() == Schema.Type.NULL).count();
  if (branches.size() != 2 || nulls != 1) {
    throw new IllegalStateException("Only [T, null] unions are supported: " + fieldSchema);
  }
}

Type guard

boolean isSimpleNullableUnion(Schema s) {
  if (s.getType() != Schema.Type.UNION) return false;
  List<Schema> ts = s.getTypes();
  return ts.size() == 2 && ts.stream().anyMatch(b -> b.getType() == Schema.Type.NULL);
}

Try / catch

try {
  Object avro = converter.convert(schema, row);
} catch (IllegalArgumentException e) {
  if (e.getMessage().startsWith("The Avro schema is not a nullable type")) {
    // fix or regenerate schema with simple nullable unions
  } else { throw e; }
}

Prevention

When it happens

Trigger: Serializing RowData into an Avro field whose schema is a union that is not a simple [T, null] or [null, T] pair, e.g. [int, string, null] or [int, long].

Common situations: Hand-written or third-party Avro schemas with multi-branch unions used as Flink sink formats; schemas evolved to add alternative types.

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


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/1494096b4f4d550e. Report an issue: GitHub.