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
- Use a concrete generated protobuf class (e.g. MyProto.MyMessage.class) with BigQueryIO.writeProtos and supply the schema explicitly via withSchema/fromClass.
- 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.
- 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
- Always pass compile-time generated protobuf classes to writeProtos.
- For runtime schemas, use BigQueryIO.write() with a custom format function plus an explicit withJsonSchema.
- Add a unit test that builds the write transform so misconfiguration fails in CI, not at runtime.
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
- A function must be provided to convert the input type into…
- BigQueryIO.Write transforms cannot be converted to a…
- Both numFileShards and auto-sharding options are set. Will…
- Both numStorageWriteApiStreams and auto-sharding options…
- Cannot convert BigQuery type '' to '' because the BigQuery…
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>() {
@OverrideView on GitHub (pinned to 12126d8942)