{"record":{"id":"ddcc1d46bcd7e329","repo":"apache/flink","slug":"the-avro-schema-is-not-a-nullable-type-s","errorCode":null,"errorMessage":"The Avro schema is not a nullable type: %s","messagePattern":"The Avro schema is not a nullable type: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"flink-formats/flink-avro/src/main/java/org/apache/flink/formats/avro/RowDataToAvroConverters.java","lineNumber":248,"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()","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-formats/flink-avro/src/main/java/org/apache/flink/formats/avro/RowDataToAvroConverters.java#L230-L266","documentation":"The nullable wrapper in RowDataToAvroConverters accepts only two-element unions of the exact shapes [T, null] or [null, T]. A UNION schema with 3+ branches (or 2 branches where neither is null) throws IllegalArgumentException('The Avro schema is not a nullable type: %s') with the full schema printed.","triggerScenarios":"A field converter receives a schema whose type is UNION but is not a simple nullable union — e.g. [\"string\",\"int\",\"null\"] or [\"string\",\"int\"] — while the RowType marks the field nullable.","commonSituations":"Hand-crafted or Avro-IDL-generated schemas with multi-alternative unions; schema evolution adding an alternative to a previously nullable field.","solutions":["Reduce the union to exactly [\"null\", T] or [T, \"null\"].","If multiple types are genuinely needed, wrap them in a record or pick one concrete type and convert data upstream.","If the column is actually non-nullable, remove the union so a plain schema reaches the converter."],"exampleFix":"// before\n{\"name\":\"v\",\"type\":[\"string\",\"int\",\"null\"]}\n\n// after\n{\"name\":\"v\",\"type\":[\"null\",\"string\"]}","handlingStrategy":"validation","validationCode":"static void validateNullableUnion(Schema s) {\n    if (s.getType() == Schema.Type.UNION) {\n        List<Schema> ts = s.getTypes();\n        boolean ok = ts.size() == 2\n                && (ts.get(0).getType() == Schema.Type.NULL || ts.get(1).getType() == Schema.Type.NULL);\n        if (!ok) throw new IllegalArgumentException(\"Not a simple nullable union: \" + s);\n    }\n}","typeGuard":"static 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(t -> t.getType() == Schema.Type.NULL);\n}","tryCatchPattern":null,"preventionTips":["Ban multi-branch unions in Avro schemas used with Flink RowData serialization.","Add a schema lint step to CI that rejects unions other than [null,T]."],"tags":["avro","flink","union","nullable","schema"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}