{"record":{"id":"6642fe31a2a0a0dd","repo":"spring-projects/spring-framework","slug":"unexpected-methodoverride-subclass","errorCode":null,"errorMessage":"Unexpected MethodOverride subclass: ","messagePattern":"Unexpected MethodOverride subclass: ","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java","lineNumber":303,"sourceCode":"\t\t\t\t\targuments.add(CodeBlock.of(\"$S\", lookupOverride.getMethodName()));\n\t\t\t\t\targuments.add(CodeBlock.of(\"$S\", lookupOverride.getBeanName()));\n\t\t\t\t\tcode.addStatement(\"$L.getMethodOverrides().addOverride(new $T($L))\", BEAN_DEFINITION_VARIABLE,\n\t\t\t\t\t\t\tLookupOverride.class, CodeBlock.join(arguments, \", \"));\n\t\t\t\t}\n\t\t\t\telse if (methodOverride instanceof ReplaceOverride replaceOverride) {\n\t\t\t\t\tCollection<CodeBlock> arguments = new ArrayList<>();\n\t\t\t\t\targuments.add(CodeBlock.of(\"$S\", replaceOverride.getMethodName()));\n\t\t\t\t\targuments.add(CodeBlock.of(\"$S\", replaceOverride.getMethodReplacerBeanName()));\n\t\t\t\t\tList<String> typeIdentifiers = replaceOverride.getTypeIdentifiers();\n\t\t\t\t\tif (!typeIdentifiers.isEmpty()) {\n\t\t\t\t\t\targuments.add(CodeBlock.of(\"java.util.List.of($S)\",\n\t\t\t\t\t\t\t\tStringUtils.collectionToDelimitedString(typeIdentifiers, \", \")));\n\t\t\t\t\t}\n\t\t\t\t\tcode.addStatement(\"$L.getMethodOverrides().addOverride(new $T($L))\", BEAN_DEFINITION_VARIABLE,\n\t\t\t\t\t\t\tReplaceOverride.class, CodeBlock.join(arguments, \", \"));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new UnsupportedOperationException(\"Unexpected MethodOverride subclass: \" +\n\t\t\t\t\t\t\tmethodOverride.getClass().getName());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate CodeBlock generateValue(@Nullable String name, @Nullable Object value) {\n\t\tPropertyNamesStack.push(name);\n\t\ttry {\n\t\t\treturn this.valueCodeGenerator.generateCode(value);\n\t\t}\n\t\tfinally {\n\t\t\tPropertyNamesStack.pop();\n\t\t}\n\t}\n\n\tprivate Class<?> getInfrastructureType(RootBeanDefinition beanDefinition) {\n\t\tif (beanDefinition.hasBeanClass()) {","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java#L285-L321","documentation":"Thrown as an UnsupportedOperationException during AOT code generation when a bean definition contains a MethodOverride that is neither a LookupOverride nor a ReplaceOverride. The generator (addMethodOverrides) only knows how to emit code for those two built-in subclasses; any other subclass (custom or from a newer Spring version) is rejected because there is no codegen path for it.","triggerScenarios":"A RootBeanDefinition has a MethodOverrides set containing an instance of a third-party or custom MethodOverride subclass, or a Spring core MethodOverride added in a newer version that this codegen branch does not handle. Triggered while BeanDefinitionPropertiesCodeGenerator.addMethodOverrides iterates the overrides.","commonSituations":"Library/framework that programmatically adds a custom MethodOverride to bean definitions; upgrading Spring core to a version that introduces a new MethodOverride type while keeping an older spring-beans (version skew); XML configuration using a custom method-replacer that surfaces as an unknown override shape.","solutions":["Identify the MethodOverride subclass named in the message and either remove it from the bean definition or replace it with LookupOverride/ReplaceOverride.","Resolve version skew: align spring-beans and spring-context (and any Spring-dependent libs) to the same Spring version so no new override type is unhandled.","If you own the override, refactor to use @Lookup or a MethodReplacer-based ReplaceOverride so the built-in codegen covers it.","Exclude the affected bean from AOT processing if it cannot be expressed with the supported override types.","Open an issue / contribute an AOT codegen branch in BeanDefinitionPropertiesCodeGenerator for the new override type."],"exampleFix":"// before: custom MethodOverride subclass the AOT generator can't emit\nbeanDefinition.getMethodOverrides().addOverride(myCustomOverride);\n\n// after: use a supported ReplaceOverride\nbeanDefinition.getMethodOverrides()\n    .addOverride(new ReplaceOverride(\"doWork\", \"workerReplacer\"));","handlingStrategy":"validation","validationCode":"// Reject unsupported MethodOverride subclasses before AOT\nfor (MethodOverride o : beanDefinition.getMethodOverrides().getOverrides()) {\n    if (!(o instanceof LookupOverride) && !(o instanceof ReplaceOverride)) {\n        throw new IllegalStateException(\n            \"Unsupported MethodOverride: \" + o.getClass().getName());\n    }\n}","typeGuard":"// Narrow to supported override types\nboolean supported = override instanceof LookupOverride || override instanceof ReplaceOverride;","tryCatchPattern":null,"preventionTips":["Align spring-context and spring-beans to the same Spring version.","Avoid custom MethodOverride subclasses; use @Lookup or a MethodReplacer.","Run AOT generation in CI to catch unsupported overrides before native build.","Review programmatic bean definitions that add method overrides."],"tags":["spring","aot","method-overrides","lookup","version-skew"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}