hibernate/hibernate-orm · error · IllegalArgumentException
Value was not an array [" + valueClass.getName() + "]
Error message
Value was not an array [" + valueClass.getName() + "]
What it means
ArrayMutabilityPlan.deepCopyNotNull snapshots arrays for dirty checking by cloning them; if the value it receives is not actually an array (Class.isArray() false) it throws this IllegalArgumentException. The class is deprecated for removal since 7.0, and the @AllowReflection note shows this is a low-level reflective copy.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java:32
* since the elements themselves are immutable, a shallow copy is enough.
*
* @author Steve Ebersole
*
* @deprecated Use {@link ImmutableObjectArrayMutabilityPlan#get()} for object arrays,
* or implement a dedicated mutability plan for primitive arrays
* (see for example {@link ShortPrimitiveArrayJavaType}'s mutability plan).
*/
@Deprecated(forRemoval = true, since = "7.0")
public class ArrayMutabilityPlan<T> extends MutableMutabilityPlan<T> {
public static final ArrayMutabilityPlan INSTANCE = new ArrayMutabilityPlan();
@SuppressWarnings({ "unchecked", "SuspiciousSystemArraycopy" })
@AllowReflection
public T deepCopyNotNull(T value) {
final var valueClass = value.getClass();
if ( !valueClass.isArray() ) {
// ugh! cannot find a way to properly define the type signature here
throw new IllegalArgumentException( "Value was not an array [" + valueClass.getName() + "]" );
}
final int length = getLength( value );
final Object copy = newInstance( valueClass.getComponentType(), length );
System.arraycopy( value, 0, copy, 0, length );
return (T) copy;
}
}
View on GitHub (pinned to fad1729dce)
Solutions
- Pair ArrayMutabilityPlan only with array-typed JavaTypes; for non-array values use MutableMutabilityPlan or ImmutableMutabilityPlan
- Since it is deprecated for removal, migrate custom types off ArrayMutabilityPlan entirely (use the mutability plan of the concrete array JavaType, e.g. primitive-array plans)
- Verify the attribute/JavaType that carries the failing MutabilityPlan actually maps X[] values
Example fix
// before
public class MyListJavaType extends AbstractClassJavaType<List<String>> {
public MyListJavaType() {
super(List.class, ArrayMutabilityPlan.INSTANCE); // List is not an array -> deep copy fails
}
}
// after
public class MyListJavaType extends AbstractClassJavaType<List<String>> {
public MyListJavaType() {
super(List.class, new MutableMutabilityPlan<>() {
@Override
protected List<String> deepCopyNotNull(List<String> value) {
return new ArrayList<>(value);
}
});
}
} Defensive patterns
Strategy: type-guard
Validate before calling
// when wiring mutability plans programmatically, assert value type matches
MutabilityPlan<?> plan = ArrayMutabilityPlan.INSTANCE;
if (plan == ArrayMutabilityPlan.INSTANCE && !attributeJavaType.isArray()) {
throw new IllegalArgumentException("ArrayMutabilityPlan requires an array JavaType");
} Type guard
static boolean isArrayValue(Object value) {
return value == null || value.getClass().isArray();
} Try / catch
try {
session.merge(entity); // triggers deep-copy snapshot
} catch (IllegalArgumentException e) {
if (String.valueOf(e.getMessage()).contains("Value was not an array")) {
// custom JavaType uses ArrayMutabilityPlan for non-array values:
// switch to MutableMutabilityPlan/ImmutableMutabilityPlan - a mapping bug, not data
throw new MappingException("Wrong mutability plan on custom type", e);
}
throw e;
} Prevention
- Match the mutability plan to the value shape: arrays get array plans, Lists get mutable plans
- The class is deprecated for removal - migrate custom types off it now
- Add a persist+merge round-trip test for every custom JavaType
- Review custom type descriptors copied from array examples
When it happens
Trigger: A custom JavaType registered ArrayMutabilityPlan.INSTANCE (or an ArrayMutabilityPlan subclass) as its MutabilityPlan but is used to manage values that are not arrays - e.g. a List, a Collection, or a scalar - so the managed-byte deep copy hits System.arraycopy on a non-array.
Common situations: Custom type descriptors copied from array examples and reused for List/plural attributes; mapping changes where the attribute type changed from X[] to List<X> without updating the mutability plan; upgrading Hibernate 6 -> 7 where this plan is deprecated and internal usage shifted.
Related errors
- Unable to determine SQL type name for column '%s' of table '
- Class '" + typeName + "' does not implement '" + supertype.g
- Could not format discriminator value to SQL string
- Unknown unwrap conversion requested: " + type.getTypeName()
- Unknown wrap conversion requested: " + conversionType.getNam
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/7d3bc7f7d19768f2.
Report an issue: GitHub.