{"record":{"id":"91587910ec9ed905","repo":"apache/beam","slug":"cannot-select-a-subfield-of-a-non-composite-type","errorCode":null,"errorMessage":"Cannot select a subfield of a non-composite type.","messagePattern":"Cannot select a subfield of a non-composite type\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/transforms/AddFields.java","lineNumber":340,"sourceCode":"          fieldType = Schema.FieldType.array(addFieldsInformation.getOutputFieldType());\n          break;\n\n        case ITERABLE:\n          addFieldsInformation =\n              getAddFieldsInformation(inputFieldType.getCollectionElementType(), nestedFields);\n          fieldType = Schema.FieldType.iterable(addFieldsInformation.getOutputFieldType());\n          break;\n\n        case MAP:\n          addFieldsInformation =\n              getAddFieldsInformation(inputFieldType.getMapValueType(), nestedFields);\n          fieldType =\n              Schema.FieldType.map(\n                  inputFieldType.getMapKeyType(), addFieldsInformation.getOutputFieldType());\n          break;\n\n        default:\n          throw new RuntimeException(\"Cannot select a subfield of a non-composite type.\");\n      }\n      fieldType = fieldType.withNullable(inputFieldType.getNullable());\n      return addFieldsInformation.toBuilder().setOutputFieldType(fieldType).build();\n    }\n\n    private static Row fillNewFields(Row row, AddFieldsInformation addFieldsInformation) {\n      Schema outputSchema = checkNotNull(addFieldsInformation.getOutputFieldType().getRowSchema());\n\n      List<Object> newValues = Lists.newArrayListWithCapacity(outputSchema.getFieldCount());\n      for (int i = 0; i < row.getFieldCount(); ++i) {\n        AddFieldsInformation nested = addFieldsInformation.getNestedNewValues().get(i);\n        if (nested != null) {\n          // New fields were added to nested subfields of this value. Recursively fill them out\n          // before adding to the new row.\n          Object newValue = fillNewFields(row.getValue(i), nested.getOutputFieldType(), nested);\n          newValues.add(newValue);\n        } else {\n          // Nothing changed. Just copy the old value into the new row.","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/transforms/AddFields.java#L322-L358","documentation":"AddFields.getAddFieldsInformation recursively computes the output type when adding fields to a nested schema. When it reaches a type that has no composite structure (not ARRAY, ITERABLE, MAP, or ROW), there is no subfield to descend into, so it throws a RuntimeException from the switch's default branch.","triggerScenarios":"Calling AddFields.<FieldName> or AddFields.field with a field path that traverses INTO a scalar leaf type, e.g. adding 'a.b' where field 'a' is a STRING or INT64 rather than a ROW/MAP/ARRAY.","commonSituations":"Typos in nested field paths (intending 'a.b' on a row schema but 'a' is actually scalar), or schemas changed upstream so a formerly-ROW field became a primitive, making the path invalid.","solutions":["Correct the field path so it only descends through composite (ROW/MAP/ARRAY) types.","Verify the input schema with pcollection.getSchema() and confirm the parent field is a ROW/MAP/ARRAY before adding a nested field.","If you want to add a top-level field, use a non-nested name like 'a_b' instead of 'a.b'.","Wrap the transform application in a try-catch for RuntimeException if schemas are dynamic, and fail gracefully."],"exampleFix":"// before (a is a STRING field)\nPCollection<Row> out = rows.apply(AddFields.<Row>field(\"a.b\", FieldType.STRING));\n// after\nPCollection<Row> out = rows.apply(AddFields.<Row>field(\"a_row.b\", FieldType.STRING));","handlingStrategy":"validation","validationCode":"Schema schema = pc.getSchema();\nSchema.Field f = schema.getField(\"a\");\nTypeName tn = f.getType().getTypeName();\nif (!EnumSet.of(TypeName.ROW, TypeName.MAP, TypeName.ARRAY, TypeName.ITERABLE).contains(tn)) {\n  throw new IllegalStateException(\"cannot descend into non-composite field 'a'\");\n}","typeGuard":"static boolean isComposite(Schema.Field f) {\n  switch (f.getType().getTypeName()) {\n    case ROW: case MAP: case ARRAY: case ITERABLE: return true;\n    default: return false;\n  }\n}","tryCatchPattern":"try { out = pc.apply(AddFields.<Row>field(path, type)); } catch (RuntimeException e) { if (e.getMessage().contains(\"non-composite\")) { /* fix path or skip */ } else throw e; }","preventionTips":["Validate nested field paths against the actual schema before applying AddFields","Re-derive schemas after upstream schema-evolving transforms","Prefer flat field names over dotted paths when the parent is scalar"],"tags":["java","apache-beam","schemas","nested-fields"],"backgroundTag":"invalid-argument-value","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}