apache/beam · error · RuntimeException
Unexpected null for value type: " +…
Error message
Unexpected null for value type: " + fieldDescriptor.getName()
What it means
The value-side counterpart in toProtoValue's MAP branch: getMapValueType() returned null for a MAP field, so entry values cannot be converted to the key/value STRUCT expected by BigQuery, and the converter throws naming the proto field.
Solutions
- Declare the field as FieldType.map(keyType, valueType) with a concrete value type.
- Use generic Map<K,V> in schema classes so the value type is inferred.
- Validate all MAP fields have both key and value types before the BigQuery sink.
- Handle the RuntimeException at row-conversion time to surface which field is malformed.
Example fix
// before FieldType m = FieldType.builder().setType(TypeName.MAP).setMapKeyType(FieldType.int64()).build(); // after FieldType m = FieldType.map(FieldType.int64(), FieldType.double_());
Defensive patterns
Strategy: validation
Validate before calling
for (Schema.Field f : schema.getFields()) {
if (f.getType().getTypeName() == Schema.TypeName.MAP && f.getType().getMapValueType() == null) {
throw new IllegalArgumentException("Field " + f.getName() + " missing map value type");
}
} Type guard
boolean hasMapValueType(Schema.Field f) {
return f.getType().getTypeName() != Schema.TypeName.MAP || f.getType().getMapValueType() != null;
} Try / catch
try {
value = toProtoValue(fieldDescriptor, beamFieldType, v);
} catch (RuntimeException e) {
if (e.getMessage().startsWith("Unexpected null for value type")) {
throw new SchemaException("Map field lacks value type: " + e.getMessage());
}
throw e;
} Prevention
- Declare full map types with FieldType.map(k, v)
- Avoid raw/non-generic Map fields
- Validate schemas before writing to BigQuery
When it happens
Trigger: toProtoValue case MAP where beamFieldType.getMapValueType() returns null while processing row data containing an actual map value.
Common situations: FieldTypes constructed with only a key type; inference from raw/non-generic Maps; schemas shared between pipeline versions where one side lacks value-type 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
- Unexpected null element type for the map's key on " +…
- Unexpected null element type for the map's value on " +…
- Unexpected null for key type: " + fieldDescriptor.getName()
- A function must be provided to convert the input type into…
- BigQueryIO.Write transforms cannot be converted to a…
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/2143a0ade618e79c.
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:353
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()
.map(
(Map.Entry<Object, Object> entry) ->
mapEntryToProtoValue(
fieldDescriptor.getMessageType(), keyType, valueType, entry))
.collect(Collectors.toList());
default:
return scalarToProtoValue(fieldDescriptor, beamFieldType, value);
}
}
private static DynamicMessage buildTimestampPicosMessage(
Descriptor timestampPicosDescriptor, Instant instant) {
long seconds = instant.getEpochSecond();
long picoseconds = instant.getNano() * 1000L; // nanos → picosView on GitHub (pinned to 12126d8942)