{"record":{"id":"12d09946289e287b","repo":"spring-projects/spring-framework","slug":"unknown-advisor-type-can-only-include-advisor","errorCode":null,"errorMessage":"Unknown advisor type {}; can only include Advisor or Advice type beans in interceptorNames chain except for last entry which may also be target instance or TargetSource","messagePattern":"Unknown advisor type (.+?); can only include Advisor or Advice type beans in interceptorNames chain except for last entry which may also be target instance or TargetSource","errorType":"exception","errorClass":"AopConfigException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java","lineNumber":558,"sourceCode":"\t\t\t\tlogger.debug(\"Refreshing target with name '\" + this.targetName + \"'\");\n\t\t\t}\n\t\t\tObject target = this.beanFactory.getBean(this.targetName);\n\t\t\treturn (target instanceof TargetSource targetSource ? targetSource : new SingletonTargetSource(target));\n\t\t}\n\t}\n\n\t/**\n\t * Convert the following object sourced from calling getBean() on a name in the\n\t * interceptorNames array to an Advisor or TargetSource.\n\t */\n\tprivate Advisor namedBeanToAdvisor(Object next) {\n\t\ttry {\n\t\t\treturn this.advisorAdapterRegistry.wrap(next);\n\t\t}\n\t\tcatch (UnknownAdviceTypeException ex) {\n\t\t\t// We expected this to be an Advisor or Advice,\n\t\t\t// but it wasn't. This is a configuration error.\n\t\t\tthrow new AopConfigException(\"Unknown advisor type \" + next.getClass() +\n\t\t\t\t\t\"; can only include Advisor or Advice type beans in interceptorNames chain \" +\n\t\t\t\t\t\"except for last entry which may also be target instance or TargetSource\", ex);\n\t\t}\n\t}\n\n\t/**\n\t * Blow away and recache singleton on an advice change.\n\t */\n\t@Override\n\tprotected void adviceChanged() {\n\t\tsuper.adviceChanged();\n\t\tif (this.singleton) {\n\t\t\tlogger.debug(\"Advice has changed; re-caching singleton instance\");\n\t\t\tsynchronized (this) {\n\t\t\t\tthis.singletonInstance = null;\n\t\t\t}\n\t\t}\n\t}","sourceCodeStart":540,"sourceCodeEnd":576,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java#L540-L576","documentation":"Thrown as AopConfigException (wrapping UnknownAdviceTypeException) by ProxyFactoryBean.namedBeanToAdvisor when an entry in interceptorNames points to a bean that is neither an Advice nor an Advisor, AND it is not allowed to be the target (i.e., targetName/targetSource was already set, or it is not the last entry). Spring expected every non-final name to be an Advice/Advisor.","triggerScenarios":"interceptorNames references a plain service/POJO bean in a position that is not the final one, or the target is already set via targetName so even the last entry must be an Advice/Advisor. The advisorAdapterRegistry.wrap call at line 553 throws UnknownAdviceTypeException, rethrown here as AopConfigException with the explanatory message.","commonSituations":"Listing the target bean in the middle of interceptorNames by mistake; using a target bean name that is shared with another non-advice bean; renaming an advice bean and forgetting to update interceptorNames; mixing targetName property with a target-as-last-entry style (now the last entry is treated as advice and fails).","solutions":["Move the target bean out of interceptorNames and use the targetName/target property instead.","Ensure every entry in interceptorNames (except optionally the last) is a bean of type Advice or Advisor.","If the last entry is meant to be the target, do NOT also set targetName/targetSource (the factory only allows one target style).","Check bean type with beanFactory.getType(name) — it must be assignable to Advice or Advisor (or be the legitimate final target)."],"exampleFix":"<!-- before -->\n<bean id=\"p\" class=\"...ProxyFactoryBean\">\n  <property name=\"targetName\" value=\"svc\"/>\n  <property name=\"interceptorNames\"><list><value>svc</value><value>loggingAdvice</value></list></property>\n</bean>\n\n<!-- after -->\n<bean id=\"p\" class=\"...ProxyFactoryBean\">\n  <property name=\"targetName\" value=\"svc\"/>\n  <property name=\"interceptorNames\"><list><value>loggingAdvice</value></list></property>\n</bean>","handlingStrategy":"validation","validationCode":"// Verify each non-final interceptor name is Advice/Advisor\nBeanFactory bf = ...;\nString[] names = pfb.getInterceptorNames();\nfor (int i = 0; i < names.length; i++) {\n  Class<?> t = bf.getType(names[i]);\n  boolean isAdvice = t != null && (Advice.class.isAssignableFrom(t) || Advisor.class.isAssignableFrom(t));\n  if (!isAdvice && i < names.length - 1) throw new IllegalStateException(names[i] + \" is not Advice/Advisor\");\n}","typeGuard":"static boolean isAdviceOrAdvisorBean(BeanFactory bf, String name) {\n  Class<?> t = bf.getType(name);\n  return t != null && (Advice.class.isAssignableFrom(t) || Advisor.class.isAssignableFrom(t));\n}","tryCatchPattern":"try { Object p = pfb.getObject(); }\ncatch (AopConfigException e) {\n  if (e.getMessage().contains(\"Unknown advisor type\")) { /* move target out of interceptorNames */ }\n  throw e;\n}","preventionTips":["Use targetName/target property for the target; reserve interceptorNames for Advice/Advisor beans.","Run beanFactory.getType(name) assertions in a smoke test of AOP configuration."],"tags":["spring-aop","proxyfactorybean","advice","configuration"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}