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

  1. Flatten STRUCT columns into individual scalar columns before writing.
  2. Remove STRUCT fields from the mutation — they are not writable via Spanner mutations.
  3. 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

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


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)