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

  1. Pair ArrayMutabilityPlan only with array-typed JavaTypes; for non-array values use MutableMutabilityPlan or ImmutableMutabilityPlan
  2. 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)
  3. 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

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


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/7d3bc7f7d19768f2. Report an issue: GitHub.