{"record":{"id":"58f49d1d83316c79","repo":"spring-projects/spring-framework","slug":"failed-to-load-class","errorCode":null,"errorMessage":"Failed to load Class [","messagePattern":"Failed to load Class \\[","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java","lineNumber":182,"sourceCode":"\t\t\t\t\t.collect(CodeBlock.joining(\", \"));\n\t\t\tcode.addStatement(format, BEAN_DEFINITION_VARIABLE, arguments);\n\t\t}\n\t}\n\n\tprivate void addInitDestroyHint(Class<?> beanUserClass, String methodName) {\n\t\tClass<?> methodDeclaringClass = beanUserClass;\n\n\t\t// Parse fully-qualified method name if necessary.\n\t\tint indexOfDot = methodName.lastIndexOf('.');\n\t\tif (indexOfDot > 0) {\n\t\t\tString className = methodName.substring(0, indexOfDot);\n\t\t\tmethodName = methodName.substring(indexOfDot + 1);\n\t\t\tif (!beanUserClass.getName().equals(className)) {\n\t\t\t\ttry {\n\t\t\t\t\tmethodDeclaringClass = ClassUtils.forName(className, beanUserClass.getClassLoader());\n\t\t\t\t}\n\t\t\t\tcatch (Throwable ex) {\n\t\t\t\t\tthrow new IllegalStateException(\"Failed to load Class [\" + className +\n\t\t\t\t\t\t\t\"] from ClassLoader [\" + beanUserClass.getClassLoader() + \"]\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tMethod method = ReflectionUtils.findMethod(methodDeclaringClass, methodName);\n\t\tif (method != null) {\n\t\t\tthis.hints.reflection().registerMethod(method, ExecutableMode.INVOKE);\n\t\t\tMethod publiclyAccessibleMethod = ClassUtils.getPubliclyAccessibleMethodIfPossible(method, beanUserClass);\n\t\t\tif (!publiclyAccessibleMethod.equals(method)) {\n\t\t\t\tthis.hints.reflection().registerMethod(publiclyAccessibleMethod, ExecutableMode.INVOKE);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void addConstructorArgumentValues(CodeBlock.Builder code, BeanDefinition beanDefinition) {\n\t\tConstructorArgumentValues constructorValues = beanDefinition.getConstructorArgumentValues();\n\t\tMap<Integer, ValueHolder> indexedValues = constructorValues.getIndexedArgumentValues();","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java#L164-L200","documentation":"Thrown as an IllegalStateException during AOT code generation when a bean's init/destroy method name is fully qualified (contains a '.' specifying a declaring class other than the bean class) and ClassUtils.forName cannot load that class through the bean class's ClassLoader. The generator (BeanDefinitionPropertiesCodeGenerator.addInitDestroyHint) parses the prefix off the method name to register a reflection hint on the real declaring class; if that class is absent from the classpath/native-image, generation aborts.","triggerScenarios":"A bean definition carries an init-method or destroy-method value like 'com.acme.Helper.init' where 'com.acme.Helper' is not resolvable by the bean class's ClassLoader at AOT build time. Triggered when BeanDefinitionPropertiesCodeGenerator is generating the bean-definition properties code and the prefix class load fails.","commonSituations":"Specifying an init/destroy method on a class that lives in an optional dependency not on the AOT classpath; renaming/moving the helper class without updating XML or @Bean(initMethod=...) values; GraalVM native-image build pruning a class that is only referenced by string in a method name; multi-module build where the module containing the declaring class is not on the compile classpath.","solutions":["Make sure the fully-qualified class used as the init/destroy method prefix is on the AOT build classpath and reachable by the bean's ClassLoader.","If the method actually lives on the bean class itself, remove the class prefix and specify just the method name.","Fix the typo or stale FQN in the XML init-method/destroy-method attribute or @Bean(initMethod=...) / destroyMethod.","If the class is intentionally optional, register a substitute init/destroy method that does not reference it, or guard the bean definition so it isn't processed when the class is absent.","Run the native build with --debug to see the exact class name and ClassLoader that failed, then add the missing artifact as a dependency."],"exampleFix":"// before: prefix class missing from native image\n@Bean(initMethod = \"com.acme.legacy.Lifecycle.start\")\npublic MyBean myBean() { ... }\n\n// after: method on the bean class itself, no FQN prefix\n@Bean(initMethod = \"start\")\npublic MyBean myBean() { ... }","handlingStrategy":"validation","validationCode":"// Validate a fully-qualified init/destroy method before AOT runs\nString m = \"com.acme.Helper.init\";\nint dot = m.lastIndexOf('.');\nif (dot <= 0) throw new IllegalArgumentException(\"Bad method spec: \" + m);\nClass<?> cls = ClassUtils.forName(m.substring(0, dot), beanClassLoader);\nAssert.notNull(ReflectionUtils.findMethod(cls, m.substring(dot + 1)),\n    () -> \"Method not found: \" + m);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer init/destroy method names without a class prefix unless the declaring class truly differs.","Add the artifact containing the declaring class to the native build dependencies.","Run a clean AOT build after refactoring class packages or names.","Lint XML/annotations for fully-qualified init/destroy method values in CI."],"tags":["spring","aot","native-image","classpath","lifecycle"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}