{"id":"8e7e5ea8abe651bb","repo":"spring-projects/spring-framework","slug":"no-methodinvocation-found-check-that-an-aop-invoc","errorCode":null,"errorMessage":"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.","messagePattern":"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\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/interceptor/ExposeInvocationInterceptor.java","lineNumber":74,"sourceCode":"\t\tpublic String toString() {\n\t\t\treturn ExposeInvocationInterceptor.class.getName() +\".ADVISOR\";\n\t\t}\n\t};\n\n\tprivate static final ThreadLocal<MethodInvocation> invocation =\n\t\t\tnew NamedThreadLocal<>(\"Current AOP method invocation\");\n\n\n\t/**\n\t * Return the AOP Alliance MethodInvocation object associated with the current invocation.\n\t * @return the invocation object associated with the current invocation\n\t * @throws IllegalStateException if there is no AOP invocation in progress,\n\t * or if the ExposeInvocationInterceptor was not added to this interceptor chain\n\t */\n\tpublic static MethodInvocation currentInvocation() throws IllegalStateException {\n\t\tMethodInvocation mi = invocation.get();\n\t\tif (mi == null) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"No MethodInvocation found: Check that an AOP invocation is in progress and that the \" +\n\t\t\t\t\t\"ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that \" +\n\t\t\t\t\t\"advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor! \" +\n\t\t\t\t\t\"In addition, ExposeInvocationInterceptor and ExposeInvocationInterceptor.currentInvocation() \" +\n\t\t\t\t\t\"must be invoked from the same thread.\");\n\t\t}\n\t\treturn mi;\n\t}\n\n\n\t/**\n\t * Ensures that only the canonical instance can be created.\n\t */\n\tprivate ExposeInvocationInterceptor() {\n\t}\n\n\t@Override\n\tpublic @Nullable Object invoke(MethodInvocation mi) throws Throwable {","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/e8729d043887bf0d0baf91e062e909b56eb2b708/spring-aop/src/main/java/org/springframework/aop/interceptor/ExposeInvocationInterceptor.java#L56-L92","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Keep ExposeInvocationInterceptor.ADVISOR in the chain (Spring registers it by default) and read currentInvocation() only on the invoking thread.","Order custom advice strictly after HIGHEST_PRECEDENCE+1 so ExposeInvocationInterceptor has already populated the thread-local.","Capture any needed invocation data on the calling thread before dispatching to another thread."],"exampleFix":"// before\n@Order(Ordered.HIGHEST_PRECEDENCE)\npublic class MyAdvice implements MethodInterceptor {\n  public Object invoke(MethodInvocation mi) throws Throwable {\n    MethodInvocation cur = ExposeInvocationInterceptor.currentInvocation(); // null -> error 108\n    return mi.proceed();\n  }\n}\n\n// after\n@Order(Ordered.HIGHEST_PRECEDENCE + 10) // after ExposeInvocationInterceptor\npublic class MyAdvice implements MethodInterceptor {\n  public Object invoke(MethodInvocation mi) throws Throwable {\n    MethodInvocation cur = ExposeInvocationInterceptor.currentInvocation();\n    return mi.proceed();\n  }\n}","handlingStrategy":"try-catch","validationCode":"boolean invocationAvailable() {\n    try {\n        ExposeInvocationInterceptor.currentInvocation();\n        return true;\n    } catch (IllegalStateException ex) {\n        return false;\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();\n} catch (IllegalStateException ex) {\n    // not in an AOP joinpoint or wrong thread; handle absence\n}","preventionTips":["Keep ExposeInvocationInterceptor.ADVISOR in every Spring proxy chain (default behaviour).","Order custom advice after HIGHEST_PRECEDENCE+1.","Never read currentInvocation() from a different thread than the invocation."],"tags":["spring-aop","invocation","threading","ordering","threadlocal"],"analyzedSha":"e8729d043887bf0d0baf91e062e909b56eb2b708","analyzedAt":"2026-08-04T19:07:39.725Z","schemaVersion":2}