apache/beam · error · RuntimeException

Unexpected null logical type " + field.getType()

Error message

Unexpected null logical type " + field.getType()

What it means

fieldDescriptorFromBeamField, for LOGICAL_TYPE fields, calls field.getType().getLogicalType() and throws "Unexpected null logical type <field type>" when null. A LOGICAL_TYPE FieldType must carry a LogicalType instance to map to a BigQuery TableFieldSchema.Type; a null result leaves the conversion impossible.

Solutions

  1. Attach the logical type: Schema.FieldType.logicalType(newyourLogicalType()) so getLogicalType() is non-null
  2. If the underlying value is a timestamp, use Schema.FieldType.DATETIME or the Beam Timestamp logical type instead
  3. Verify schemas aren't losing their logical-type argument during serialization between pipeline stages
  4. Inspect the printed field.getType() in the message to see which field is malformed

Example fix

// before
Schema.FieldType lt = Schema.FieldType.of(TypeName.LOGICAL_TYPE);
// after
Schema.FieldType lt = Schema.FieldType.logicalType(new SqlTypes.LocalTimestamp());
Defensive patterns

Strategy: validation

Validate before calling

// Java
for (Schema.Field f : beamSchema.getFields()) {
  if (f.getType().getTypeName() == TypeName.LOGICAL_TYPE && f.getType().getLogicalType() == null) {
    throw new IllegalStateException("LOGICAL_TYPE field without logical type: " + f.getName());
  }
}

Type guard

// Java
LogicalType<?, ?> lt = fieldType.getLogicalType();
if (lt != null) { /* safe to map to TableFieldSchema.Type */ }

Try / catch

// Java
try {
  descriptor = BeamRowToStorageApiProto.protoTableSchemaFromBeamSchema(beamSchema);
} catch (RuntimeException e) {
  if (e.getMessage().startsWith("Unexpected null logical type")) {
    // attach the LogicalType instance to the field
  }
  throw e;
}

Prevention

When it happens

Trigger: A Beam Schema field declared with TypeName.LOGICAL_TYPE but constructed without a LogicalType argument (e.g. Schema.FieldType.logicalType(null) or bare logical type name), then written to BigQuery Storage API

Common situations: Custom logical-type handling code, schemas serialized/deserialized across job versions losing the logical-type argument, or manual FieldType construction.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/04b93aa1cb1e5b83. 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:249

                field.getName());
        Preconditions.checkState(
            !(containedTypeName.isCollectionType() || containedTypeName.isMapType()),
            "Nested container types are not supported by BigQuery. Field %s contains a type %s",
            field.getName(),
            containedTypeName.name());
        TableFieldSchema elementFieldSchema =
            fieldDescriptorFromBeamField(Field.of(field.getName(), elementType));
        builder = builder.setType(elementFieldSchema.getType());
        if (elementFieldSchema.hasTimestampPrecision()) {
          builder = builder.setTimestampPrecision(elementFieldSchema.getTimestampPrecision());
        }
        builder.addAllFields(elementFieldSchema.getFieldsList());
        builder = builder.setMode(TableFieldSchema.Mode.REPEATED);
        break;
      case LOGICAL_TYPE:
        @Nullable LogicalType<?, ?> logicalType = field.getType().getLogicalType();
        if (logicalType == null) {
          throw new RuntimeException("Unexpected null logical type " + field.getType());
        }
        @Nullable TableFieldSchema.Type type;
        if (logicalType.getIdentifier().equals(Timestamp.IDENTIFIER)) {
          int precision =
              Preconditions.checkNotNull(
                  logicalType.getArgument(),
                  "Expected logical type argument for timestamp precision.");
          if (precision != 9) {
            throw new RuntimeException(
                "Unsupported precision for Timestamp logical type " + precision);
          }
          // Map Timestamp.NANOS logical type to BigQuery TIMESTAMP(12) for nanosecond precision
          type = TableFieldSchema.Type.TIMESTAMP;
          builder.setTimestampPrecision(Int64Value.newBuilder().setValue(12L).build());
        } else {
          type = LOGICAL_TYPES.get(logicalType.getIdentifier());
          if (type == null) {
            throw new RuntimeException("Unsupported logical type " + field.getType());

View on GitHub (pinned to 12126d8942)