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
- Do not mutate pipeline elements; create and emit a new instance with the changes.
- Make element types deeply immutable (final fields, immutable collections, builder pattern).
- Check collections inside elements — replace with immutable copies.
- 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
- Treat pipeline elements as deeply immutable
- Use builder pattern or records for element types
- Copy collections instead of mutating them
- Never cache and mutate elements across output calls in DoFns
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
- cannot encode a null Byte
- cannot encode a null Double
- cannot encode a null Float
- cannot encode a null Instant
- cannot encode a null Iterable
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)