apache/beam · error · RuntimeException

Unexpected null element type: " + fieldDescriptor.getName()

Error message

Unexpected null element type: " + fieldDescriptor.getName()

What it means

toProtoValue converts Beam values into protobuf values per field. For ARRAY/ITERABLE typed fields it fetches the collection element type with getCollectionElementType(); a null result means the FieldType claims to be a collection but carries no element type, so per-element conversion is impossible and the converter throws, naming the proto field.

Solutions

  1. Use FieldType.array(elementType) (or FieldType.iterable(elementType)) with a concrete element type.
  2. Replace raw java.util.List fields in schema classes with generic List<T> so inference captures the element type.
  3. Validate the schema before the sink: assert getCollectionElementType() != null for all ARRAY/ITERABLE fields.
  4. Catch the RuntimeException around toProtoValue and fail the record with a clear schema error.

Example fix

// before
FieldType arr = FieldType.builder().setType(TypeName.ARRAY).build();
// after
FieldType arr = FieldType.array(FieldType.string());
Defensive patterns

Strategy: validation

Validate before calling

for (Schema.Field f : schema.getFields()) {
  Schema.FieldType t = f.getType();
  if ((t.getTypeName() == Schema.TypeName.ARRAY || t.getTypeName() == Schema.TypeName.ITERABLE)
      && t.getCollectionElementType() == null) {
    throw new IllegalArgumentException("Field " + f.getName() + " missing collection element type");
  }
}

Type guard

boolean hasCollectionElementType(Schema.Field f) {
  return (f.getType().getTypeName() != Schema.TypeName.ARRAY
      && f.getType().getTypeName() != Schema.TypeName.ITERABLE)
      || f.getType().getCollectionElementType() != null;
}

Try / catch

try {
  value = toProtoValue(fieldDescriptor, beamFieldType, v);
} catch (RuntimeException e) {
  if (e.getMessage().startsWith("Unexpected null element type")) {
    throw new SchemaException("Array field lacks element type: " + e.getMessage());
  }
  throw e;
}

Prevention

When it happens

Trigger: toProtoValue for a field whose beamFieldType.getTypeName() is ARRAY or ITERABLE while beamFieldType.getCollectionElementType() returns null - malformed FieldType built without an element type, or an Iterable typed via reflection without element info.

Common situations: Schemas using Iterable fields inferred from raw/generic Java types (e.g. List without generics), hand-built FieldTypes with TypeName.ARRAY but no collection element, deserialized schemas losing element metadata.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BeamRowToStorageApiProto.java:339

      } else {
        throw new IllegalArgumentException(
            "Received null value for non-nullable field " + fieldDescriptor.getName());
      }
    }
    return toProtoValue(fieldDescriptor, beamField.getType(), value);
  }

  private static Object toProtoValue(
      FieldDescriptor fieldDescriptor, FieldType beamFieldType, Object value) {
    switch (beamFieldType.getTypeName()) {
      case ROW:
        return messageFromBeamRow(fieldDescriptor.getMessageType(), (Row) value, null, -1);
      case ARRAY:
      case ITERABLE:
        Iterable<Object> iterable = (Iterable<Object>) value;
        @Nullable FieldType iterableElementType = beamFieldType.getCollectionElementType();
        if (iterableElementType == null) {
          throw new RuntimeException("Unexpected null element type: " + fieldDescriptor.getName());
        }

        return StreamSupport.stream(iterable.spliterator(), false)
            .map(v -> toProtoValue(fieldDescriptor, iterableElementType, v))
            .collect(Collectors.toList());
      case MAP:
        Map<Object, Object> map = (Map<Object, Object>) value;
        @Nullable FieldType keyType = beamFieldType.getMapKeyType();
        @Nullable FieldType valueType = beamFieldType.getMapValueType();
        if (keyType == null) {
          throw new RuntimeException("Unexpected null for key type: " + fieldDescriptor.getName());
        }
        if (valueType == null) {
          throw new RuntimeException(
              "Unexpected null for value type: " + fieldDescriptor.getName());
        }

        return map.entrySet().stream()

View on GitHub (pinned to 12126d8942)