{"record":{"id":"1494096b4f4d550e","repo":"apache/iceberg","slug":"the-avro-schema-is-not-a-nullable-type-schema-t","errorCode":null,"errorMessage":"The Avro schema is not a nullable type: ${schema.toString()}","messagePattern":"The Avro schema is not a nullable type: (.+?)","errorType":"exception","errorClass":"java.lang.IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"flink/v2.1/flink/src/main/java/org/apache/iceberg/flink/formats/avro/RowDataToAvroConverters.java","lineNumber":292,"sourceCode":"      private static final long serialVersionUID = 1L;\n\n      @Override\n      public Object convert(Schema schema, Object object) {\n        if (object == null) {\n          return null;\n        }\n\n        // get actual schema if it is a nullable schema\n        Schema actualSchema;\n        if (schema.getType() == Schema.Type.UNION) {\n          List<Schema> types = schema.getTypes();\n          int size = types.size();\n          if (size == 2 && types.get(1).getType() == Schema.Type.NULL) {\n            actualSchema = types.get(0);\n          } else if (size == 2 && types.get(0).getType() == Schema.Type.NULL) {\n            actualSchema = types.get(1);\n          } else {\n            throw new IllegalArgumentException(\n                \"The Avro schema is not a nullable type: \" + schema.toString());\n          }\n        } else {\n          actualSchema = schema;\n        }\n        return converter.convert(actualSchema, object);\n      }\n    };\n  }\n\n  private static RowDataToAvroConverter createRowConverter(\n      RowType rowType, boolean legacyTimestampMapping) {\n    final RowDataToAvroConverter[] fieldConverters =\n        rowType.getChildren().stream()\n            .map(legacyType -> createConverter(legacyType, legacyTimestampMapping))\n            .toArray(RowDataToAvroConverter[]::new);\n    final LogicalType[] fieldTypes =\n        rowType.getFields().stream().map(RowType.RowField::getType).toArray(LogicalType[]::new);","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/flink/v2.1/flink/src/main/java/org/apache/iceberg/flink/formats/avro/RowDataToAvroConverters.java#L274-L310","documentation":"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.","triggerScenarios":"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].","commonSituations":"Hand-written or third-party Avro schemas with multi-branch unions used as Flink sink formats; schemas evolved to add alternative types.","solutions":["Restructure the Avro schema so nullable fields are unions of exactly one type plus null","Wrap non-null multi-branch unions into a single record/managed representation before serialization","Flatten or split the union field into separate columns in the source table","Validate the Avro schema's unions before wiring it as a sink format"],"exampleFix":"// before\n{\"type\":[\"null\",\"int\",\"string\"]}\n// after\n{\"type\":[\"null\",\"string\"]}","handlingStrategy":"validation","validationCode":"Schema fieldSchema = schema.getField(\"f\").schema();\nif (fieldSchema.getType() == Schema.Type.UNION) {\n  List<Schema> branches = fieldSchema.getTypes();\n  long nulls = branches.stream().filter(s -> s.getType() == Schema.Type.NULL).count();\n  if (branches.size() != 2 || nulls != 1) {\n    throw new IllegalStateException(\"Only [T, null] unions are supported: \" + fieldSchema);\n  }\n}","typeGuard":"boolean isSimpleNullableUnion(Schema s) {\n  if (s.getType() != Schema.Type.UNION) return false;\n  List<Schema> ts = s.getTypes();\n  return ts.size() == 2 && ts.stream().anyMatch(b -> b.getType() == Schema.Type.NULL);\n}","tryCatchPattern":"try {\n  Object avro = converter.convert(schema, row);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().startsWith(\"The Avro schema is not a nullable type\")) {\n    // fix or regenerate schema with simple nullable unions\n  } else { throw e; }\n}","preventionTips":["Generate Avro schemas from table schemas instead of hand-writing unions","Restrict schemas to [T, null] unions for nullable fields","Validate schemas with AvroSchemaConverter before production use","Avoid schema evolution that introduces multi-branch unions"],"tags":["flink","avro","schema","union"],"backgroundTag":"schema-validation-failed","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}