apache/beam · error · IllegalArgumentException

DynamicMessage is not supported.

Error message

DynamicMessage is not supported.

What it means

BigQueryIO.writeProtos(Class) refuses DynamicMessage as the proto message class. DynamicMessage carries no compile-time schema, so Beam cannot reliably map protobuf field types to BigQuery types (e.g. INT64 could be INT64, TIME, DATETIME, or TIMESTAMP). The library throws IllegalArgumentException immediately to force callers to use a concrete generated protobuf message class.

Solutions

  1. Use a concrete generated protobuf class (e.g. MyProto.MyMessage.class) with BigQueryIO.writeProtos and supply the schema explicitly via withSchema/fromClass.
  2. If the schema is genuinely dynamic, use BigQueryIO.write() with withFormatFunction(FormatProto.fromDynamicMessage(...)) or a custom function converting the DynamicMessage to a TableRow, and set the schema explicitly with withJsonSchema.
  3. Never pass DynamicMessage.class; add an upfront check that the class does not equal DynamicMessage.class.

Example fix

// before
Write<DynamicMessage> w = BigQueryIO.writeProtos(DynamicMessage.class);
// after
Write<MyProto.Event> w = BigQueryIO.writeProtos(MyProto.Event.class)
    .withSchema(MySchemaUtil.forProto(MyProto.Event.class));
Defensive patterns

Strategy: validation

Validate before calling

if (DynamicMessage.class.equals(protoMessageClass)) {
  throw new IllegalArgumentException("Use a generated protobuf class with writeProtos, not DynamicMessage.class");
}
Write<T> w = BigQueryIO.writeProtos(protoMessageClass);

Type guard

static <T extends Message> boolean isSupportedProtoClass(Class<T> c) {
  return c != null && !DynamicMessage.class.equals(c) && Message.class.isAssignableFrom(c);
}

Try / catch

try {
  write = BigQueryIO.writeProtos(protoClass);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("DynamicMessage is not supported")) {
    write = BigQueryIO.<T>write().withFormatFunction(formatFromDynamicMessage);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling BigQueryIO.writeProtos(DynamicMessage.class), or passing a variable/class reference that resolves to DynamicMessage.class instead of a generated protobuf message class.

Common situations: Developers building pipelines where the proto schema is only known at runtime (e.g. reading descriptor sets or Confluent Schema Registry) and mistakenly use DynamicMessage with writeProtos instead of the typed write() path with a custom format function.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java:2629

    return BigQueryIO.<GenericRecord>write()
        .withAvroFormatFunction(GENERIC_RECORD_IDENTITY_FORMATTER);
  }

  /**
   * A {@link PTransform} that writes a {@link PCollection} containing protocol buffer objects to a
   * BigQuery table. If using one of the storage-api write methods, these protocol buffers must
   * match the schema of the table.
   *
   * <p>If a Schema is provided using {@link Write#withSchema}, that schema will be used for
   * creating the table if necessary. If no schema is provided, one will be inferred from the
   * protocol buffer's descriptor. Note that inferring a schema from the protocol buffer may not
   * always provide the intended schema as multiple BigQuery types can map to the same protocol
   * buffer type. For example, a protocol buffer field of type INT64 may be an INT64 BigQuery type,
   * but it might also represent a TIME, DATETIME, or a TIMESTAMP type.
   */
  public static <T extends Message> Write<T> writeProtos(Class<T> protoMessageClass) {
    if (DynamicMessage.class.equals(protoMessageClass)) {
      throw new IllegalArgumentException("DynamicMessage is not supported.");
    }
    try {
      return BigQueryIO.<T>write().toBuilder()
          .setFormatFunction(FormatProto.fromClass(protoMessageClass))
          .build()
          .withWriteProtosClass(protoMessageClass);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  abstract static class TableRowFormatFunction<T>
      implements SerializableBiFunction<
          TableRowToStorageApiProto.@Nullable SchemaInformation, T, TableRow> {
    static <T> TableRowFormatFunction<T> fromSerializableFunction(
        SerializableFunction<T, TableRow> serializableFunction) {
      return new TableRowFormatFunction<T>() {
        @Override

View on GitHub (pinned to 12126d8942)