apache/beam · error · RuntimeException

Unexpected null for key type: " + fieldDescriptor.getName()

Error message

Unexpected null for key type: " + fieldDescriptor.getName()

What it means

For MAP-typed values in toProtoValue, the key and value FieldTypes are retrieved to convert each entry. A null key type means the map's FieldType lacks key-type metadata; since each BigQuery key/value struct entry needs both converters, the converter throws immediately, naming the proto field.

Solutions

  1. Rebuild the field with FieldType.map(keyType, valueType).
  2. Use generic Map<K,V> fields in schema classes so inference records the key type.
  3. Pre-validate: for each MAP field check getMapKeyType() != null before writing.
  4. Catch and translate the RuntimeException to a schema-validation error for the offending field.

Example fix

// before
FieldType m = FieldType.builder().setType(TypeName.MAP).setMapValueType(FieldType.string()).build();
// after
FieldType m = FieldType.map(FieldType.string(), FieldType.string());
Defensive patterns

Strategy: validation

Validate before calling

for (Schema.Field f : schema.getFields()) {
  if (f.getType().getTypeName() == Schema.TypeName.MAP && f.getType().getMapKeyType() == null) {
    throw new IllegalArgumentException("Field " + f.getName() + " missing map key type");
  }
}

Type guard

boolean hasMapKeyType(Schema.Field f) {
  return f.getType().getTypeName() != Schema.TypeName.MAP || f.getType().getMapKeyType() != null;
}

Try / catch

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

Prevention

When it happens

Trigger: toProtoValue case MAP where beamFieldType.getMapKeyType() returns null - i.e. the FieldType is TypeName.MAP but was not created with a key type (not via FieldType.map(k, v)).

Common situations: Row data carrying Map values whose schema field was assembled without key type; schema inference from raw Map types; schema evolution dropping map 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/ca53aa5a6a4ef78e. 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:350

      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()
            .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(

View on GitHub (pinned to 12126d8942)