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

  1. Create logical-type fields via FieldType.logicalType(LogicalType) so the LogicalType instance is attached.
  2. Attach the logical type with withLogicalType() on an existing FieldType rather than setting the raw TypeName.
  3. Pre-validate: for each LOGICAL_TYPE field assert getLogicalType() != null before writing.
  4. 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

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


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