{"record":{"id":"e13adaeeb2cfdb70","repo":"apache/beam","slug":"fieldtype-type-must-be-either-a-row-or-a-container","errorCode":null,"errorMessage":"FieldType <type> must be either a row or a container containing rows","messagePattern":"FieldType <type> must be either a row or a container containing rows","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/FieldAccessDescriptor.java","lineNumber":616,"sourceCode":"        fieldType = fieldType.getCollectionElementType();\n      } else if (fieldType.getTypeName().isMapType()) {\n        fieldType = fieldType.getMapValueType();\n      }\n    }\n    return getFieldSchema(fieldType);\n  }\n\n  private static Schema getFieldSchema(FieldType type) {\n    if (TypeName.ROW.equals(type.getTypeName())) {\n      return type.getRowSchema();\n    } else if (type.getTypeName().isCollectionType()) {\n      return getFieldSchema(type.getCollectionElementType());\n    } else if (TypeName.MAP.equals(type.getTypeName())) {\n      return getFieldSchema(type.getMapValueType());\n    } else if (TypeName.LOGICAL_TYPE.equals(type.getTypeName())) {\n      return getFieldSchema(type.getLogicalType().getBaseType());\n    } else {\n      throw new IllegalArgumentException(\n          \"FieldType \" + type + \" must be either a row or a container containing rows\");\n    }\n  }\n\n  private static void validateFieldDescriptor(Schema schema, FieldDescriptor fieldDescriptor) {\n    Integer fieldId = fieldDescriptor.getFieldId();\n    if (fieldId != null) {\n      if (fieldId < 0 || fieldId >= schema.getFieldCount()) {\n        throw new IllegalArgumentException(\"Invalid field id \" + fieldId + \" for schema \" + schema);\n      }\n    }\n    // If qualifiers were specified, validate them.\n    // For example, if a selector was a[*][*], then a needs to be a List of a List.\n    Field field =\n        (fieldId != null)\n            ? schema.getField(fieldId)\n            : schema.getField(fieldDescriptor.getFieldName());\n    FieldType fieldType = field.getType();","sourceCodeStart":598,"sourceCodeEnd":634,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/FieldAccessDescriptor.java#L598-L634","documentation":"FieldAccessDescriptor.getFieldSchema recursively descends a FieldType to find the nested row schema it references. It accepts rows, containers of rows, map value types, and logical-type base types; anything else has no row schema and triggers this IllegalArgumentException. It means a field-selection qualifier navigated into a type that has no schema to select from.","triggerScenarios":"Calling FieldAccessDescriptor.withFieldNames/withMatchingFields resolved against a schema where the accessed field (after container/logical unwrapping) is a primitive — e.g. selecting nested fields 'a.b' where a is an INT64, or indexing into a container of primitives.","commonSituations":"Schema field selection strings that over-navigate (field[0].x on List<String>); schema drift after changing a field from a row to a primitive; using getFieldSchema directly on a leaf FieldType.","solutions":["Fix the field-access qualifier so nested selection only traverses row-typed (or row-container) fields.","Validate the target Schema field types before resolving the descriptor.","If the underlying schema changed, update FieldAccessDescriptor usage to match the new structure.","Catch IllegalArgumentException from resolve() and report a user-facing schema-mapping error."],"exampleFix":"// before: 'user.name' where user is STRING\nFieldAccessDescriptor.withFieldNames(\"user.name\")\n\n// after: select only existing row fields\nFieldAccessDescriptor.withFieldNames(\"user\")","handlingStrategy":"validation","validationCode":"Schema.Field f = schema.getField(\"user\");\nif (!f.getType().getTypeName().isCompositeType() && f.getType().getTypeName() != Schema.TypeName.ROW)\n  throw new IllegalArgumentException(\"field 'user' is not row-typed; cannot select nested fields\");","typeGuard":"static boolean isRowNavigable(FieldType t) {\n  return t.getTypeName() == TypeName.ROW || t.getTypeName() == TypeName.ARRAY || t.getTypeName() == TypeName.ITERABLE || t.getTypeName() == TypeName.MAP || t.getTypeName() == TypeName.LOGICAL_TYPE;\n}","tryCatchPattern":"try { descriptor.resolve(schema); }\ncatch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"must be either a row or a container\")) {\n    throw new IllegalArgumentException(\"Field selection navigates into a non-row field; fix qualifier\", e);\n  } throw e;\n}","preventionTips":["Keep field-selection strings in sync with the schema definition","Validate descriptors in unit tests against the actual schema","Avoid over-navigating qualifiers into primitive fields"],"tags":["beam-schemas","field-selection","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}