spring-projects/spring-framework · error · IllegalArgumentException

Target class [{target.getClass().getName()}] not assignable

Error message

Target class [{target.getClass().getName()}] not assignable to editable class [{editable.getName()}]

What it means

Thrown by BeanUtils.copyProperties(source, target, editable, ignoreProperties) when an 'editable' restriction class/interface is supplied but the target object is not an instance of it. Spring uses the editable class to decide which properties to copy, so it first validates that target is assignable to editable. This is a programming-contract IllegalArgumentException (not a BeansException) thrown before any copying happens.

Source

Thrown at spring-beans/src/main/java/org/springframework/beans/BeanUtils.java:805

	 * when matching properties in the source and target objects. See the
	 * documentation for {@link #copyProperties(Object, Object)} for details.
	 * @param source the source bean
	 * @param target the target bean
	 * @param editable the class (or interface) to restrict property setting to
	 * @param ignoreProperties array of property names to ignore
	 * @throws BeansException if the copying failed
	 * @see BeanWrapper
	 */
	private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
			String @Nullable ... ignoreProperties) throws BeansException {

		Assert.notNull(source, "Source must not be null");
		Assert.notNull(target, "Target must not be null");

		Class<?> actualEditable = target.getClass();
		if (editable != null) {
			if (!editable.isInstance(target)) {
				throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
						"] not assignable to editable class [" + editable.getName() + "]");
			}
			actualEditable = editable;
		}
		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
		Set<String> ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null);
		CachedIntrospectionResults sourceResults = (actualEditable != source.getClass() ?
				CachedIntrospectionResults.forClass(source.getClass()) : null);

		for (PropertyDescriptor targetPd : targetPds) {
			Method writeMethod = targetPd.getWriteMethod();
			if (writeMethod != null && (ignoredProps == null || !ignoredProps.contains(targetPd.getName()))) {
				PropertyDescriptor sourcePd = (sourceResults != null ?
						sourceResults.getPropertyDescriptor(targetPd.getName()) : targetPd);
				if (sourcePd != null) {
					Method readMethod = sourcePd.getReadMethod();
					if (readMethod != null) {
						if (isAssignable(writeMethod, readMethod, sourcePd, targetPd)) {

View on GitHub (pinned to 69bf83ad71)

Solutions

  1. Make the target object's class implement/extend the class passed as 'editable', or pass an interface the target already implements.
  2. Pass null as editable to copy from all properties of the target's actual class instead of restricting to an editable type.
  3. Pass editable = target.getClass() (or a superclass/interface of it) so isInstance(target) holds.

Example fix

// before
BeanUtils.copyProperties(src, target, SomeInterface.class);
// target does not implement SomeInterface

// after - restrict to a type target implements
BeanUtils.copyProperties(src, target, TargetType.class);
// or no restriction
BeanUtils.copyProperties(src, target);
Defensive patterns

Strategy: validation

Validate before calling

// before calling the editable-restricted copyProperties
Class<?> editable = SomeInterface.class;
if (editable != null && !editable.isInstance(target)) {
    throw new IllegalArgumentException(
        "target is not a " + editable.getName());
}
BeanUtils.copyProperties(src, target, editable);

Type guard

static boolean targetIsEditable(Object target, Class<?> editable) {
    return editable == null || editable.isInstance(target);
}

Prevention

When it happens

Trigger: Calling BeanUtils.copyProperties(Object source, Object target, Class<?> editable) (or the variant with ignoreProperties) where editable is a class/interface that target does not implement or extend. For example passing editable = SomeInterface.class while target is a concrete class that does not implement SomeInterface.

Common situations: Passing the wrong class literal as the editable argument (e.g. passing the source's class instead of an interface the target implements), copy-pasting a copyProperties call and forgetting to update the class, or refactoring a target bean to no longer implement an interface while copyProperties calls still reference it.

Related errors


AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09). Data as JSON: /api/errors/107d638f21b00fde. Report an issue: GitHub.