apache/beam · error · IllegalArgumentException
Structs are not supported in mutation.
Error message
Structs are not supported in mutation.
What it means
MutationSizeEstimator.sizeOf() estimates the byte size of a Spanner mutation for batching. Spanner mutations cannot contain STRUCT values, so when a mutation's value list has a STRUCT-typed column the estimator throws IllegalArgumentException as a guard.
Solutions
- Flatten STRUCT columns into individual scalar columns before writing.
- Remove STRUCT fields from the mutation — they are not writable via Spanner mutations.
- If the struct data is needed, serialize it to STRING (e.g. JSON) in a normal column.
Example fix
// before
mutationBuilder.set("address").to(structValue) // STRUCT not allowed
// after
mutationBuilder.set("address_json").to(Value.string(JsonUtils.toString(structValue))) Defensive patterns
Strategy: validation
Validate before calling
boolean hasStruct = mutation.size() > 0 && mutation.getValues().stream()
.anyMatch(v -> v.getType().getCode() == Type.Code.STRUCT);
if (hasStruct) throw new IllegalStateException("flatten struct columns before writing"); Type guard
boolean structFree(Iterable<Value> values) { return StreamSupport.stream(values.spliterator(), false).noneMatch(v -> v.getType().getCode() == Type.Code.STRUCT); } Try / catch
try { size = MutationSizeEstimator.of(mutation); } catch (IllegalArgumentException e) { /* drop/flatten struct columns */ } Prevention
- Never forward STRUCT columns into Spanner mutation writes
- Pre-serialize nested data to JSON STRING columns
- Test pipelines against the real target schema including nested types
When it happens
Trigger: Building a Spanner mutation (insert/update/delete) whose row values include a STRUCT column, then passing it through the Beam Spanner sink which estimates mutation size.
Common situations: Source schema (e.g. Spanner query results or another database) includes STRUCT columns and they are forwarded directly to the Spanner write without flattening; auto-generated pipelines copying full table rows.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Unknown key part
- Unknown mutation operation type
- Unknown type
- Unsupported field type
- Unsupported logical type in key
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/5c40226c77d91298.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/spanner/MutationSizeEstimator.java:48
/** Estimates the logical size of {@link com.google.cloud.spanner.Mutation}. */
class MutationSizeEstimator {
// Prevent construction.
private MutationSizeEstimator() {}
/** Estimates a size of mutation in bytes. */
static long sizeOf(Mutation m) {
if (m.getOperation() == Mutation.Op.DELETE) {
return sizeOf(m.getKeySet());
}
long result = 0;
for (Value v : m.getValues()) {
switch (v.getType().getCode()) {
case ARRAY:
result += estimateArrayValue(v);
break;
case STRUCT:
throw new IllegalArgumentException("Structs are not supported in mutation.");
default:
result += estimatePrimitiveValue(v);
}
}
return result;
}
private static long sizeOf(KeySet keySet) {
long result = 0;
for (Key k : keySet.getKeys()) {
result += sizeOf(k);
}
for (KeyRange kr : keySet.getRanges()) {
result += sizeOf(kr);
}
return result;
}
View on GitHub (pinned to 12126d8942)