apache/beam · critical · IllegalMutationException

Value mutated illegally, new value was . Encoding was , now…

Error message

Value %s mutated illegally, new value was %s. Encoding was %s, now %s.

What it means

IllegalMutationException signals that a value supposed to be immutable was changed after it was handed to Beam. The detector re-encodes both the original and current value; if the encodings differ, the value was mutated, which breaks Beam's assumption that elements are immutable and can corrupt pipelines.

Solutions

  1. Do not mutate pipeline elements; create and emit a new instance with the changes.
  2. Make element types deeply immutable (final fields, immutable collections, builder pattern).
  3. Check collections inside elements — replace with immutable copies.
  4. If mutation is intentional, emit a new value rather than reusing the mutated object.

Example fix

// before
value.getMetrics().add(x); // mutates element
// after
MyValue updated = value.toBuilder().addMetric(x).build();
c.emit(updated);
Defensive patterns

Strategy: validation

Validate before calling

// detect mutation before handing to Beam:
byte[] before = CoderUtils.encodeToBase64(coder, value);
/* ... use value ... */
if (!before.equals(CoderUtils.encodeToBase64(coder, value))) throw new IllegalStateException("element mutated");

Type guard

// Java lacks runtime type guards; enforce at compile time:
// declare element fields final, use List.copyOf()/Map.copyOf() wrappers

Try / catch

try { detector.close(); } catch (IllegalMutationException e) { log.error("Element mutated illegally: {}", e.getMessage()); }

Prevention

When it happens

Trigger: Calling verifyUnmodified()/close() on a MutationDetectors detector after mutating the element (e.g. modifying a List/Map field, changing a field of a POJO, mutating an Avro record in place).

Common situations: DoFns or transform code that mutates input elements before output; mutable collections in custom types; Java records/POJOs with setters used in pipeline elements.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/util/MutationDetectors.java:157

      T possiblyModifiedClonedValue = CoderUtils.clone(coder, possiblyModifiedObject);
      Object newStructuralValue = coder.structuralValue(possiblyModifiedClonedValue);
      if (originalStructuralValue.equals(newStructuralValue)) {
        return;
      } else if (Objects.deepEquals(
          encodedOriginalObject, CoderUtils.encodeToByteArray(coder, possiblyModifiedObject))) {
        LOG.warn(
            "{} of type {} has a #structuralValue method which does not return true when the "
                + "encoding of the elements is equal. Element {}",
            Coder.class.getSimpleName(),
            coder.getClass(),
            possiblyModifiedObject);
        return;
      }
      illegalMutation(clonedOriginalObject, possiblyModifiedClonedValue);
    }

    private void illegalMutation(T previousValue, T newValue) throws CoderException {
      throw new IllegalMutationException(
          String.format(
              "Value %s mutated illegally, new value was %s." + " Encoding was %s, now %s.",
              previousValue,
              newValue,
              CoderUtils.encodeToBase64(coder, previousValue),
              CoderUtils.encodeToBase64(coder, newValue)),
          previousValue,
          newValue);
    }

    @Override
    public void close() {
      verifyUnmodified();
    }
  }
}

View on GitHub (pinned to 12126d8942)