spring-projects/spring-framework · error · IllegalStateException

No MethodInvocation found: Check that an AOP invocation is i

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

ExposeInvocationInterceptor.currentInvocation() throws IllegalStateException when the thread-local holding the current MethodInvocation is null. ExposeInvocationInterceptor (order HIGHEST_PRECEDENCE+1) sets the thread-local as it enters the chain; this error means either there is no AOP invocation in progress, the interceptor is not in the chain, the call happened on a different thread, or an advice with PriorityOrdered.HIGHEST_PRECEDENCE ran before it and tried to read the invocation.

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 e8729d0438)

Solutions

  1. Keep ExposeInvocationInterceptor.ADVISOR in the chain (Spring registers it by default) and read currentInvocation() only on the invoking thread.
  2. Order custom advice strictly after HIGHEST_PRECEDENCE+1 so ExposeInvocationInterceptor has already populated the thread-local.
  3. Capture any needed invocation data on the calling thread before dispatching to another thread.

Example fix

// before
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MyAdvice implements MethodInterceptor {
  public Object invoke(MethodInvocation mi) throws Throwable {
    MethodInvocation cur = ExposeInvocationInterceptor.currentInvocation(); // null -> error 108
    return mi.proceed();
  }
}

// after
@Order(Ordered.HIGHEST_PRECEDENCE + 10) // after ExposeInvocationInterceptor
public class MyAdvice implements MethodInterceptor {
  public Object invoke(MethodInvocation mi) throws Throwable {
    MethodInvocation cur = ExposeInvocationInterceptor.currentInvocation();
    return mi.proceed();
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

boolean invocationAvailable() {
    try {
        ExposeInvocationInterceptor.currentInvocation();
        return true;
    } catch (IllegalStateException ex) {
        return false;
    }
}

Try / catch

try {
    MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
} catch (IllegalStateException ex) {
    // not in an AOP joinpoint or wrong thread; handle absence
}

Prevention

When it happens

Trigger: Calling ExposeInvocationInterceptor.currentInvocation() from a non-proxied call, from a worker/CompletableFuture thread, or from an advice ordered HIGHEST_PRECEDENCE that executes ahead of ExposeInvocationInterceptor.

Common situations: Accessing the invocation inside @Async/@Scheduled tasks that hop threads; custom PriorityOrdered advice that needs the joinpoint; AspectJ pointcuts used outside a proxied bean; serialisation/deserialisation losing the thread context.

Related errors


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