spring-projects/spring-framework · error · IllegalStateException
No MethodInvocation found: Check that an AOP invocation is…
Error message
No MethodInvocation found: Check that an AOP invocation is in progress and that the ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor! In addition, ExposeInvocationInterceptor and ExposeInvocationInterceptor.currentInvocation() must be invoked from the same thread.
What it means
Thrown by ExposeInvocationInterceptor.currentInvocation() (line 72-79) when the thread-local holding the current MethodInvocation is empty. The invocation is exposed only while ExposeInvocationInterceptor is on the chain and actively proceeding; if no AOP invocation is in progress, the interceptor was not added (or was ordered after the caller), or currentInvocation() is called from a different thread than the proxy invocation, the thread-local is null and an IllegalStateException is raised.
Solutions
- Ensure ExposeInvocationInterceptor (or its ADVISOR singleton) is in the interceptor chain - Spring adds it automatically for most proxies; for manual ProxyFactory, add pf.addAdvice(0, ExposeInvocationInterceptor.INSTANCE).
- Do not call currentInvocation() from a different thread than the proxy invocation; capture needed data on the invoking thread and pass it along.
- Lower your advice precedence so it runs after ExposeInvocationInterceptor: avoid HIGHEST_PRECEDENCE for advices that need the invocation; ExposeInvocationInterceptor itself is HIGHEST_PRECEDENCE+1.
- Only call currentInvocation() while a proxied method is actually executing.
Example fix
// before // manual proxy, expose interceptor omitted ProxyFactory pf = new ProxyFactory(target); pf.addAdvice(myAspect); // calls currentInvocation() -> null // after ProxyFactory pf = new ProxyFactory(target); pf.addAdvice(ExposeInvocationInterceptor.INSTANCE); // first pf.addAdvice(myAspect);
Defensive patterns
Strategy: validation
Validate before calling
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
boolean exposed;
try { ExposeInvocationInterceptor.currentInvocation(); exposed = true; }
catch (IllegalStateException e) { exposed = false; }
if (!exposed) { /* add ExposeInvocationInterceptor to the chain */ } Try / catch
try {
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
// use mi
} catch (IllegalStateException e) {
// no invocation exposed: fall back or reconfigure chain
} Prevention
- Keep ExposeInvocationInterceptor near the front of the chain (it is HIGHEST_PRECEDENCE+1).
- Do not call currentInvocation() from a different thread than the proxy call.
- Avoid HIGHEST_PRECEDENCE on advices that need the exposed invocation.
When it happens
Trigger: Calling ExposeInvocationInterceptor.currentInvocation() outside any proxied method call, from a background thread that was not the invoking thread, or from an advice whose order is HIGHEST_PRECEDENCE (which runs before ExposeInvocationInterceptor, whose order is HIGHEST_PRECEDENCE+1). Also when the pointcut/aspect tries to read the invocation but the expose interceptor was never added to the chain.
Common situations: Using AspectJ 'bean()' pointcut or code that reads MethodInvocation.currentInvocation() while ExposeInvocationInterceptor is missing. An advice annotated @Order(HIGHEST_PRECEDENCE) running before the expose interceptor. Spawning work onto another thread inside an advised method and calling currentInvocation() there.
Related errors
- Advisor sorting failed with unexpected bean creation…
- AOP configuration seems to be invalid: tried calling method
- Cannot find current proxy: Set 'exposeProxy' property on…
- DeclarePrecedence not presently supported in Spring AOP
- Null return value from advice does not match primitive…
AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09).
Data as JSON: /api/errors/8e7e5ea8abe651bb.
Report an issue: GitHub.
Appendix: source
Thrown at spring-aop/src/main/java/org/springframework/aop/interceptor/ExposeInvocationInterceptor.java:74
public String toString() {
return ExposeInvocationInterceptor.class.getName() +".ADVISOR";
}
};
private static final ThreadLocal<MethodInvocation> invocation =
new NamedThreadLocal<>("Current AOP method invocation");
/**
* Return the AOP Alliance MethodInvocation object associated with the current invocation.
* @return the invocation object associated with the current invocation
* @throws IllegalStateException if there is no AOP invocation in progress,
* or if the ExposeInvocationInterceptor was not added to this interceptor chain
*/
public static MethodInvocation currentInvocation() throws IllegalStateException {
MethodInvocation mi = invocation.get();
if (mi == null) {
throw new IllegalStateException(
"No MethodInvocation found: Check that an AOP invocation is in progress and that the " +
"ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that " +
"advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor! " +
"In addition, ExposeInvocationInterceptor and ExposeInvocationInterceptor.currentInvocation() " +
"must be invoked from the same thread.");
}
return mi;
}
/**
* Ensures that only the canonical instance can be created.
*/
private ExposeInvocationInterceptor() {
}
@Override
public @Nullable Object invoke(MethodInvocation mi) throws Throwable {View on GitHub (pinned to 69bf83ad71)