{"record":{"id":"7d3bc7f7d19768f2","repo":"hibernate/hibernate-orm","slug":"value-was-not-an-array-valueclass-getname","errorCode":null,"errorMessage":"Value was not an array [\" + valueClass.getName() + \"]","messagePattern":"Value was not an array \\[\" \\+ valueClass\\.getName\\(\\) \\+ \"\\]","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java","lineNumber":32,"sourceCode":" * since the elements themselves are immutable, a shallow copy is enough.\n *\n * @author Steve Ebersole\n *\n * @deprecated Use {@link ImmutableObjectArrayMutabilityPlan#get()} for object arrays,\n *             or implement a dedicated mutability plan for primitive arrays\n *             (see for example {@link ShortPrimitiveArrayJavaType}'s mutability plan).\n */\n@Deprecated(forRemoval = true, since = \"7.0\")\npublic class ArrayMutabilityPlan<T> extends MutableMutabilityPlan<T> {\n\tpublic static final ArrayMutabilityPlan INSTANCE = new ArrayMutabilityPlan();\n\n\t@SuppressWarnings({ \"unchecked\", \"SuspiciousSystemArraycopy\" })\n\t@AllowReflection\n\tpublic T deepCopyNotNull(T value) {\n\t\tfinal var valueClass = value.getClass();\n\t\tif ( !valueClass.isArray() ) {\n\t\t\t// ugh!  cannot find a way to properly define the type signature here\n\t\t\tthrow new IllegalArgumentException( \"Value was not an array [\" + valueClass.getName() + \"]\" );\n\t\t}\n\t\tfinal int length = getLength( value );\n\t\tfinal Object copy = newInstance( valueClass.getComponentType(), length );\n\t\tSystem.arraycopy( value, 0, copy, 0, length );\n\t\treturn (T) copy;\n\t}\n}\n","sourceCodeStart":14,"sourceCodeEnd":40,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ArrayMutabilityPlan.java#L14-L40","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\npublic class MyListJavaType extends AbstractClassJavaType<List<String>> {\n    public MyListJavaType() {\n        super(List.class, ArrayMutabilityPlan.INSTANCE); // List is not an array -> deep copy fails\n    }\n}\n// after\npublic class MyListJavaType extends AbstractClassJavaType<List<String>> {\n    public MyListJavaType() {\n        super(List.class, new MutableMutabilityPlan<>() {\n            @Override\n            protected List<String> deepCopyNotNull(List<String> value) {\n                return new ArrayList<>(value);\n            }\n        });\n    }\n}","handlingStrategy":"type-guard","validationCode":"// when wiring mutability plans programmatically, assert value type matches\nMutabilityPlan<?> plan = ArrayMutabilityPlan.INSTANCE;\nif (plan == ArrayMutabilityPlan.INSTANCE && !attributeJavaType.isArray()) {\n    throw new IllegalArgumentException(\"ArrayMutabilityPlan requires an array JavaType\");\n}","typeGuard":"static boolean isArrayValue(Object value) {\n    return value == null || value.getClass().isArray();\n}","tryCatchPattern":"try {\n    session.merge(entity); // triggers deep-copy snapshot\n} catch (IllegalArgumentException e) {\n    if (String.valueOf(e.getMessage()).contains(\"Value was not an array\")) {\n        // custom JavaType uses ArrayMutabilityPlan for non-array values:\n        // switch to MutableMutabilityPlan/ImmutableMutabilityPlan - a mapping bug, not data\n        throw new MappingException(\"Wrong mutability plan on custom type\", e);\n    }\n    throw e;\n}","preventionTips":["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"],"tags":["hibernate","orm","custom-type","mutability-plan","dirty-checking","deprecated-api"],"backgroundTag":"mutability-plan-type-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}