{"record":{"id":"4d527f8285b0723b","repo":"pentaho/pentaho-kettle","slug":"avroinput-error-unionerror2","errorCode":null,"errorMessage":"AvroInput.Error.UnionError2","messagePattern":"AvroInput\\.Error\\.UnionError2","errorType":"exception","errorClass":"KettleException","httpStatus":null,"severity":"error","filePath":"plugins/avro-format/core/src/main/java/org/pentaho/di/trans/steps/avro/input/AvroNestedReader.java","lineNumber":870,"sourceCode":"    List<Schema> types = s.getTypes();\n\n    // the type other than null\n    Schema otherSchema = null;\n\n    if ( types.size() != 2 ) {\n      throw new KettleException( BaseMessages.getString( PKG, \"AvroInput.Error.UnionError1\" ) );\n    }\n\n    for ( Schema p : types ) {\n      if ( p.getType() == Schema.Type.NULL ) {\n        ok = true;\n      } else {\n        otherSchema = p;\n      }\n    }\n\n    if ( !ok ) {\n      throw new KettleException( BaseMessages.getString( PKG, \"AvroInput.Error.UnionError2\" ) );\n    }\n\n    return otherSchema;\n  }\n\n  /**\n   * Cleanses a string path by ensuring that any variables names present in the path do not contain \".\"s (replaces any\n   * dots with underscores).\n   *\n   * @param path the path to cleanse\n   * @return the cleansed path\n   */\n  public static String cleansePath( String path ) {\n    // look for variables and convert any \".\" to \"_\"\n\n    int index = path.indexOf( \"${\" );\n\n    int endIndex = 0;","sourceCodeStart":852,"sourceCodeEnd":888,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/plugins/avro-format/core/src/main/java/org/pentaho/di/trans/steps/avro/input/AvroNestedReader.java#L852-L888","documentation":"Kettle exception thrown by checkUnion when a two-type union does not include NULL. The reader expects nullable unions ['null', X]; a union of two non-null types (e.g. ['int','string']) has no null branch and cannot be processed.","triggerScenarios":"checkUnion iterates union branches looking for Schema.Type.NULL; no branch is NULL so the 'ok' flag remains false and the exception is thrown (the non-null branch is returned otherwise).","commonSituations":"Files written with non-nullable two-way unions; schema regenerated without the null default; data producers that avoid Avro null-default convention; Kafka Avro records with [\"int\",\"long\"] style unions.","solutions":["Change the union to include a null branch: [\"null\", <type>], typically with null as the default branch.","Pick one concrete type and remove the union if both branches are non-null.","Normalize the schema upstream (schema resolution/evolution step) before Pentaho reads it.","Verify the configured path targets the right field; a neighboring field may actually be the nullable one."],"exampleFix":"// before\n{\"name\":\"v\",\"type\":[\"int\",\"long\"]}\n// after\n{\"name\":\"v\",\"type\":[\"null\",\"long\"],\"default\":null}","handlingStrategy":"validation","validationCode":"// Require a NULL branch in any union before reading\nList<Schema> types = fieldSchema.getTypes();\nif (types.size() == 2 && types.stream().noneMatch(t -> t.getType() == Schema.Type.NULL)) {\n  throw new IllegalArgumentException(\"Union ['\" + types.get(0) + \"','\" + types.get(1) + \"'] lacks a null branch\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  value = reader.convertToKettleValue(record, schema, field);\n} catch (KettleException e) {\n  if (e.getMessage().contains(\"UnionError2\")) {\n    logError(\"Union has no null branch — add 'null' to the union in the schema\", e);\n  }\n}","preventionTips":["Adopt the Avro null-default convention: [\"null\", T] with \"default\": null","Reject non-nullable two-way unions at schema registration time","Diff writer schemas against your read schema before onboarding new producers"],"tags":["avro","union-type","nullable"],"backgroundTag":"schema-validation-failed","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}