{"id":"8e6388b210507ee1","repo":"spring-projects/spring-framework","slug":"cannot-find-current-proxy-set-exposeproxy-prope","errorCode":null,"errorMessage":"Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.","messagePattern":"Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and ensure that AopContext\\.currentProxy\\(\\) is invoked in the same thread as the AOP invocation context\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/framework/AopContext.java","lineNumber":70,"sourceCode":"\n\n\tprivate AopContext() {\n\t}\n\n\n\t/**\n\t * Try to return the current AOP proxy. This method is usable only if the\n\t * calling method has been invoked via AOP, and the AOP framework has been set\n\t * to expose proxies. Otherwise, this method will throw an IllegalStateException.\n\t * @return the current AOP proxy (never returns {@code null})\n\t * @throws IllegalStateException if the proxy cannot be found, because the\n\t * method was invoked outside an AOP invocation context, or because the\n\t * AOP framework has not been configured to expose the proxy\n\t */\n\tpublic static Object currentProxy() throws IllegalStateException {\n\t\tObject proxy = currentProxy.get();\n\t\tif (proxy == null) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and \" +\n\t\t\t\t\t\t\t\"ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.\");\n\t\t}\n\t\treturn proxy;\n\t}\n\n\t/**\n\t * Make the given proxy available via the {@code currentProxy()} method.\n\t * <p>Note that the caller should be careful to keep the old value as appropriate.\n\t * @param proxy the proxy to expose (or {@code null} to reset it)\n\t * @return the old proxy, which may be {@code null} if none was bound\n\t * @see #currentProxy()\n\t */\n\tstatic @Nullable Object setCurrentProxy(@Nullable Object proxy) {\n\t\tObject old = currentProxy.get();\n\t\tif (proxy != null) {\n\t\t\tcurrentProxy.set(proxy);\n\t\t}","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/e8729d043887bf0d0baf91e062e909b56eb2b708/spring-aop/src/main/java/org/springframework/aop/framework/AopContext.java#L52-L88","documentation":"Thrown by AopContext.currentProxy() when the per-thread ThreadLocal holding the current AOP proxy is null. Spring does not expose the proxy by default (performance cost); it only populates the ThreadLocal when ProxyConfig.exposeProxy=true. The error means either exposeProxy was not enabled, or currentProxy() was called from a different thread than the one running the AOP invocation (the ThreadLocal is set/restored around the proxy invocation only).","triggerScenarios":"Calling AopContext.currentProxy() from target code when exposeProxy=false; calling it from a newly-spawned thread, a CompletableFuture/scheduler, or after the original invocation returned; calling it outside any proxied method invocation at all.","commonSituations":"Self-invocation of an advised method where the developer wants the proxy (e.g. to hit @Transactional on another method of the same bean); async hand-off to another thread loses the ThreadLocal; reactive/reactor pipelines where the continuation runs on a different thread.","solutions":["Enable exposeProxy on the proxy config: <aop:aspectj-autoproxy expose-proxy=\"true\"/> or @EnableAspectJAutoProxy(exposeProxy=true), or ProxyConfig.setExposeProxy(true).","Avoid the pattern entirely by injecting the proxy/self reference into the bean and calling through it, or refactor self-invocation into a separate bean.","Ensure currentProxy() is called synchronously within the same thread and stack frame as the proxied method body.","For async/reactive flows, capture the proxy before switching threads and pass it explicitly, since the ThreadLocal will not transfer."],"exampleFix":"// before\n@Configuration\n@EnableAspectJAutoProxy\n// ... in target code\nAopContext.currentProxy();  // throws\n\n// after\n@Configuration\n@EnableAspectJAutoProxy(exposeProxy = true)\n// ... in target code, same thread as the proxied call\nMyService self = (MyService) AopContext.currentProxy();\nself.otherTransactionalMethod();","handlingStrategy":"validation","validationCode":"// Verify before invoking, in the same thread as the proxied call:\nObject proxy = AopContext.currentProxy(); // will throw if not exposed; guard instead:\nboolean exposed = ...; // from @EnableAspectJAutoProxy(exposeProxy=true)\n// Prefer: inject the proxy bean reference and avoid AopContext entirely.","typeGuard":null,"tryCatchPattern":"try {\n  Object proxy = AopContext.currentProxy();\n} catch (IllegalStateException ex) {\n  // exposeProxy not set, or wrong thread; fall back to injected self reference\n}","preventionTips":["Enable exposeProxy globally only if self-invocation-through-proxy is unavoidable; otherwise inject the proxy bean.","Never call currentProxy() from a different thread/reactive scheduler than the AOP invocation.","Capture the proxy before async hand-off and pass it explicitly."],"tags":["spring-aop","expose-proxy","threadlocal","self-invocation"],"analyzedSha":"e8729d043887bf0d0baf91e062e909b56eb2b708","analyzedAt":"2026-08-04T19:07:39.725Z","schemaVersion":2}