{"record":{"id":"65314082c1a09056","repo":"apache/iceberg","slug":"the-avro-schema-is-not-a-nullable-type-schema-653140","errorCode":null,"errorMessage":"The Avro schema is not a nullable type: \" + schema.toString()","messagePattern":"The Avro schema is not a nullable type: \" \\+ schema\\.toString\\(\\)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"flink/v2.2/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.2/flink/src/main/java/org/apache/iceberg/flink/formats/avro/RowDataToAvroConverters.java#L274-L310","documentation":"The nullable-wrapping converter in RowDataToAvroConverters expects the Avro schema for a field to be either a plain type or a union of exactly two branches where one is NULL. When convert() encounters a union that is not of the shape [T, null] or [null, T] (e.g. a 3-branch union, a single non-null type, or a union of two non-null types), it throws IllegalArgumentException.","triggerScenarios":"Calling convert(schema, object) where schema is a UNION type with more than 2 branches, or 2 branches neither of which is NULL, or where the converter was not built for a nullable schema.","commonSituations":"Hand-written or third-party Avro schemas with multi-branch unions (e.g. [null, int, string]) fed to a RowData-to-Avro writer; schema evolved to add extra union branches; passing the outer union instead of a branch schema.","solutions":["Flatten the Avro schema so nullable fields are unions of exactly one type plus null, e.g. [\"null\", \"int\"].","Extract the matching non-null branch schema and pass that to convert() when handling a branch yourself.","Regenerate/normalize the schema from the Flink/LogicalType via AvroSchemaConverter.convertToSchema instead of supplying a custom union schema.","If multi-branch unions are required, map them to a Flink ROW with nullable fields before serialization."],"exampleFix":"// before\n{\"type\":[\"null\",\"int\",\"string\"]}\n// after\n{\"type\":[\"null\",\"string\"]} // single nullable type per field","handlingStrategy":"validation","validationCode":"Schema actual = schema;\nif (actual.getType() == Schema.Type.UNION) {\n  List<Schema> types = actual.getTypes();\n  boolean ok = types.size() == 2 && (types.get(0).getType() == Schema.Type.NULL || types.get(1).getType() == Schema.Type.NULL);\n  if (!ok) throw new IllegalArgumentException(\"Field must be [T, null] union, got: \" + actual);\n}","typeGuard":null,"tryCatchPattern":"try { return converter.convert(schema, value); } catch (IllegalArgumentException e) { /* normalize the schema (flatten unions) and retry once */ }","preventionTips":["Generate Avro schemas via AvroSchemaConverter.convertToSchema rather than hand-writing unions.","Keep nullable fields as exactly [\"null\", T] unions.","Validate third-party schemas on ingestion before registering them for writers."],"tags":["flink","avro","schema","union-type"],"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"}