apache/beam · error · IllegalArgumentException

FieldType must be either a row or a container containing…

Error message

FieldType <type> must be either a row or a container containing rows

What it means

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.

Solutions

  1. Fix the field-access qualifier so nested selection only traverses row-typed (or row-container) fields.
  2. Validate the target Schema field types before resolving the descriptor.
  3. If the underlying schema changed, update FieldAccessDescriptor usage to match the new structure.
  4. Catch IllegalArgumentException from resolve() and report a user-facing schema-mapping error.

Example fix

// before: 'user.name' where user is STRING
FieldAccessDescriptor.withFieldNames("user.name")

// after: select only existing row fields
FieldAccessDescriptor.withFieldNames("user")
Defensive patterns

Strategy: validation

Validate before calling

Schema.Field f = schema.getField("user");
if (!f.getType().getTypeName().isCompositeType() && f.getType().getTypeName() != Schema.TypeName.ROW)
  throw new IllegalArgumentException("field 'user' is not row-typed; cannot select nested fields");

Type guard

static boolean isRowNavigable(FieldType t) {
  return t.getTypeName() == TypeName.ROW || t.getTypeName() == TypeName.ARRAY || t.getTypeName() == TypeName.ITERABLE || t.getTypeName() == TypeName.MAP || t.getTypeName() == TypeName.LOGICAL_TYPE;
}

Try / catch

try { descriptor.resolve(schema); }
catch (IllegalArgumentException e) {
  if (e.getMessage().contains("must be either a row or a container")) {
    throw new IllegalArgumentException("Field selection navigates into a non-row field; fix qualifier", e);
  } throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/e13adaeeb2cfdb70. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/FieldAccessDescriptor.java:616

        fieldType = fieldType.getCollectionElementType();
      } else if (fieldType.getTypeName().isMapType()) {
        fieldType = fieldType.getMapValueType();
      }
    }
    return getFieldSchema(fieldType);
  }

  private static Schema getFieldSchema(FieldType type) {
    if (TypeName.ROW.equals(type.getTypeName())) {
      return type.getRowSchema();
    } else if (type.getTypeName().isCollectionType()) {
      return getFieldSchema(type.getCollectionElementType());
    } else if (TypeName.MAP.equals(type.getTypeName())) {
      return getFieldSchema(type.getMapValueType());
    } else if (TypeName.LOGICAL_TYPE.equals(type.getTypeName())) {
      return getFieldSchema(type.getLogicalType().getBaseType());
    } else {
      throw new IllegalArgumentException(
          "FieldType " + type + " must be either a row or a container containing rows");
    }
  }

  private static void validateFieldDescriptor(Schema schema, FieldDescriptor fieldDescriptor) {
    Integer fieldId = fieldDescriptor.getFieldId();
    if (fieldId != null) {
      if (fieldId < 0 || fieldId >= schema.getFieldCount()) {
        throw new IllegalArgumentException("Invalid field id " + fieldId + " for schema " + schema);
      }
    }
    // If qualifiers were specified, validate them.
    // For example, if a selector was a[*][*], then a needs to be a List of a List.
    Field field =
        (fieldId != null)
            ? schema.getField(fieldId)
            : schema.getField(fieldDescriptor.getFieldName());
    FieldType fieldType = field.getType();

View on GitHub (pinned to 12126d8942)