spring-projects/spring-framework · error · IllegalStateException

MethodInvocation is not a Spring ProxyMethodInvocation: {}

Error message

MethodInvocation is not a Spring ProxyMethodInvocation: {}

What it means

Thrown inside AspectJExpressionPointcut.matches(method, targetClass, args) when the current MethodInvocation obtained from ExposeInvocationInterceptor is not a ProxyMethodInvocation. The 3-arg matches is invoked at runtime for dynamic (runtime) pointcut checks and needs the proxy reference to bind 'this' correctly.

Source

Thrown at spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java:359

	public boolean isRuntime() {
		return obtainPointcutExpression().mayNeedDynamicTest();
	}

	@Override
	public boolean matches(Method method, Class<?> targetClass, @Nullable Object... args) {
		ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);

		// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
		// consistent with return of MethodInvocationProceedingJoinPoint
		ProxyMethodInvocation pmi = null;
		Object targetObject = null;
		Object thisObject = null;
		try {
			MethodInvocation curr = ExposeInvocationInterceptor.currentInvocation();
			if (curr.getMethod() == method) {
				targetObject = curr.getThis();
				if (!(curr instanceof ProxyMethodInvocation currPmi)) {
					throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + curr);
				}
				pmi = currPmi;
				thisObject = pmi.getProxy();
			}
		}
		catch (IllegalStateException ex) {
			// No current invocation...
			if (logger.isDebugEnabled()) {
				logger.debug("Could not access current invocation - matching with limited context: " + ex);
			}
		}

		try {
			JoinPointMatch joinPointMatch = shadowMatch.matchesJoinPoint(thisObject, targetObject, args);

			/*
			 * Do a final check to see if any this(TYPE) kind of residue match. For
			 * this purpose, we use the original method's (proxy method's) shadow to

View on GitHub (pinned to e8729d0438)

Solutions

  1. Ensure advice chains run exclusively through Spring's proxy infrastructure so the current invocation is always a ProxyMethodInvocation.
  2. Adapt third-party invocations to ProxyMethodInvocation before exposing them via ExposeInvocationInterceptor.
  3. Avoid mixing non-Spring MethodInvocation types in a Spring-advised object graph.

Example fix

// No user code change typical; ensure the proxy is created by Spring:
// before: custom invocation pushed into ExposeInvocationInterceptor

// after: use Spring's ProxyFactory to build the proxy
ProxyFactory pf = new ProxyFactory(target);
pf.addAdvice(myInterceptor);
MyService proxy = (MyService) pf.getProxy();
Defensive patterns

Strategy: type-guard

Validate before calling

MethodInvocation curr = ExposeInvocationInterceptor.currentInvocation();
if (!(curr instanceof ProxyMethodInvocation)) {
    throw new IllegalStateException("current invocation must be a ProxyMethodInvocation");
}

Type guard

boolean ok = ExposeInvocationInterceptor.currentInvocation() instanceof org.springframework.aop.ProxyMethodInvocation;

Prevention

When it happens

Trigger: A runtime (dynamic) pointcut evaluation where the current invocation in the thread-local ExposeInvocationInterceptor is a non-Spring MethodInvocation, e.g. from a third-party interceptor chain or a custom advisory framework plugged into Spring.

Common situations: Integrating non-Spring AOP interceptors within a Spring-advised proxy; replacing the default interceptor chain; using Spring AOP infrastructure in an embedded scripting environment that supplies its own invocation.

Related errors


AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04). Data as JSON: /data/errors/ebd472a50125d95a.json. Report an issue: GitHub.