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
- Change the method visibility from private to package-private or protected (public also works) so CGLIB can override and delegate it.
- 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.
- 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
- Keep advised (proxied) methods package-private, protected, or public.
- Do not put @Transactional/@Async on private methods with proxy-based AOP.
- Use AspectJ weaving if you truly need to advise private methods.
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
- Could not generate CGLIB subclass of
- MethodInvocation is not a Spring ProxyMethodInvocation
- TargetSource cannot determine target class: Either an…
- Unable to instantiate proxy using Objenesis, and regular…
- Unexpected AOP exception
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#hashCodeView on GitHub (pinned to 69bf83ad71)