spring-projects/spring-framework · error · IllegalStateException

Need to invoke method

Error message

Need to invoke method '%s' found on proxy for target class '%s' but cannot be delegated to target bean. Switch its visibility to package or protected.

What it means

Thrown by AopUtils.selectInvocableMethod (line 152-158) when the method selected for invocation is private and non-static and the target type is a SpringProxy (a CGLIB-based AOP proxy). CGLIB subclasses the target and cannot override or delegate private methods to the actual target instance, so the call would execute on the proxy subclass rather than the real bean; Spring therefore rejects it with an IllegalStateException and asks you to widen visibility.

Solutions

  1. Change the method visibility from private to package-private or protected (public also works) so CGLIB can override and delegate it.
  2. Move the cross-cutting concern to a public method and have the private helper call it, or use AspectJ load-time weaving (which can advise private methods) instead of Spring proxy-based AOP.
  3. Ensure proxyTargetClass is intentional; interface-based (JDK dynamic) proxies avoid CGLIB private-method issues but cannot proxy non-interface methods at all.

Example fix

// before
@Service
public class OrderService {
    @Transactional
    private void save(Order o) { /* ... */ } // CGLIB cannot delegate
}

// after
@Service
public class OrderService {
    @Transactional
    protected void save(Order o) { /* ... */ }
}
Defensive patterns

Strategy: validation

Validate before calling

import java.lang.reflect.Modifier;
static boolean aopInvocable(Method m, Class<?> targetType) {
    int mod = m.getModifiers();
    return !Modifier.isPrivate(mod) || Modifier.isStatic(mod)
        || !org.springframework.aop.SpringProxy.class.isAssignableFrom(targetType);
}

Prevention

When it happens

Trigger: Invoking a private method on a bean that gets class-based (CGLIB) proxied - e.g. a private @Transactional, @Async, or other advised method, or programmatic AOP selecting a private method via selectInvocableMethod. Also triggered when introspection resolves an advised joinpoint to a private method on the proxy subclass.

Common situations: Making a @Transactional or @Async method private (Spring AOP cannot intercept private methods). Refactoring a public method to package/private by mistake. Using class-based proxying (proxyTargetClass=true) on beans with private advised methods.

Related errors


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

Appendix: source

Thrown at spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java:154

	 * Select an invocable method on the target type: either the given method itself
	 * if actually exposed on the target type, or otherwise a corresponding method
	 * on one of the target type's interfaces or on the target type itself.
	 * @param method the method to check
	 * @param targetType the target type to search methods on (typically an AOP proxy)
	 * @return a corresponding invocable method on the target type
	 * @throws IllegalStateException if the given method is not invocable on the given
	 * target type (typically due to a proxy mismatch)
	 * @since 4.3
	 * @see MethodIntrospector#selectInvocableMethod(Method, Class)
	 */
	public static Method selectInvocableMethod(Method method, @Nullable Class<?> targetType) {
		if (targetType == null) {
			return method;
		}
		Method methodToUse = MethodIntrospector.selectInvocableMethod(method, targetType);
		if (Modifier.isPrivate(methodToUse.getModifiers()) && !Modifier.isStatic(methodToUse.getModifiers()) &&
				SpringProxy.class.isAssignableFrom(targetType)) {
			throw new IllegalStateException(String.format(
					"Need to invoke method '%s' found on proxy for target class '%s' but cannot " +
					"be delegated to target bean. Switch its visibility to package or protected.",
					method.getName(), method.getDeclaringClass().getSimpleName()));
		}
		return methodToUse;
	}

	/**
	 * Determine whether the given method is an "equals" method.
	 * @see java.lang.Object#equals
	 */
	public static boolean isEqualsMethod(@Nullable Method method) {
		return ReflectionUtils.isEqualsMethod(method);
	}

	/**
	 * Determine whether the given method is a "hashCode" method.
	 * @see java.lang.Object#hashCode

View on GitHub (pinned to 69bf83ad71)