apache/beam · error · RuntimeException
Unexpectedly null logical type " + beamFieldType
Error message
Unexpectedly null logical type " + beamFieldType
What it means
scalarToProtoValue handles primitive and logical-type scalars. For a LOGICAL_TYPE field it calls beamFieldType.getLogicalType(); if that returns null the FieldType claims a logical type but carries no LogicalType instance (identifier/argument metadata missing), making value conversion undefined, so it throws.
Source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BeamRowToStorageApiProto.java:389
long picoseconds = instant.getNano() * 1000L; // nanos → picos
return DynamicMessage.newBuilder(timestampPicosDescriptor)
.setField(
Preconditions.checkNotNull(timestampPicosDescriptor.findFieldByName("seconds")),
seconds)
.setField(
Preconditions.checkNotNull(timestampPicosDescriptor.findFieldByName("picoseconds")),
picoseconds)
.build();
}
@VisibleForTesting
static Object scalarToProtoValue(
@Nullable FieldDescriptor fieldDescriptor, FieldType beamFieldType, Object value) {
if (beamFieldType.getTypeName() == TypeName.LOGICAL_TYPE) {
@Nullable LogicalType<?, ?> logicalType = beamFieldType.getLogicalType();
if (logicalType == null) {
throw new RuntimeException("Unexpectedly null logical type " + beamFieldType);
}
if (logicalType.getIdentifier().equals(Timestamp.IDENTIFIER)) {
Instant instant = (Instant) value;
Descriptor timestampPicosDescriptor =
Preconditions.checkNotNull(fieldDescriptor).getMessageType();
return buildTimestampPicosMessage(timestampPicosDescriptor, instant);
}
@Nullable
BiFunction<LogicalType<?, ?>, Object, Object> logicalTypeEncoder =
LOGICAL_TYPE_ENCODERS.get(logicalType.getIdentifier());
if (logicalTypeEncoder == null) {
throw new RuntimeException("Unsupported logical type " + logicalType.getIdentifier());
}
return logicalTypeEncoder.apply(logicalType, value);
} else {
@Nullable
Function<Object, Object> encoder = PRIMITIVE_ENCODERS.get(beamFieldType.getTypeName());
if (encoder == null) {View on GitHub (pinned to 12126d8942)
Solutions
- Create logical-type fields via FieldType.logicalType(LogicalType) so the LogicalType instance is attached.
- Attach the logical type with withLogicalType() on an existing FieldType rather than setting the raw TypeName.
- Pre-validate: for each LOGICAL_TYPE field assert getLogicalType() != null before writing.
- Regenerate the schema from the record class instead of reusing a serialized one.
Example fix
// before FieldType lt = FieldType.builder().setType(TypeName.LOGICAL_TYPE).build(); // after FieldType lt = FieldType.logicalType(Timestamp.of(TimeUnit.NANOSECONDS));
Defensive patterns
Strategy: validation
Validate before calling
for (Schema.Field f : schema.getFields()) {
if (f.getType().getTypeName() == Schema.TypeName.LOGICAL_TYPE && f.getType().getLogicalType() == null) {
throw new IllegalArgumentException("Field " + f.getName() + " has LOGICAL_TYPE without a LogicalType instance");
}
} Type guard
boolean hasLogicalTypeInstance(Schema.Field f) {
return f.getType().getTypeName() != Schema.TypeName.LOGICAL_TYPE || f.getType().getLogicalType() != null;
} Try / catch
try {
value = toProtoValue(fieldDescriptor, beamFieldType, v);
} catch (RuntimeException e) {
if (e.getMessage().startsWith("Unexpectedly null logical type")) {
throw new SchemaException("Logical type metadata missing on field: " + e.getMessage());
}
throw e;
} Prevention
- Create logical-type fields only via FieldType.logicalType(...)
- Never set TypeName.LOGICAL_TYPE directly on a builder
- Rebuild schemas from record classes rather than serializing/deserializing them across versions
When it happens
Trigger: scalarToProtoValue (via toProtoValue) receiving a FieldType whose getTypeName() == TypeName.LOGICAL_TYPE but whose getLogicalType() is null - e.g. a FieldType built with setType(TypeName.LOGICAL_TYPE) directly instead of FieldType.logicalType(...), or one reconstructed from a lossy serialization.
Common situations: Hand-built logical-type FieldTypes bypassing the factory method; schemas copied between Beam versions where logical type metadata is incompatible; reflection-based schema assembly.
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
- Unsupported logical type " + field.getType()
- Unsupported logical type " + logicalType.getIdentifier()
- Standard logical type '%s' does not have a static of('%s') m
- Error instantiating logical type '%s' with of('%s') method.
- RenameFields does not support renaming logical types.
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/925aeab81fc80909.
Report an issue: GitHub.