spring-projects/spring-framework · error · UnsupportedOperationException
Unexpected MethodOverride subclass:
Error message
Unexpected MethodOverride subclass:
What it means
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.
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.
Example fix
// before: custom MethodOverride subclass the AOT generator can't emit
beanDefinition.getMethodOverrides().addOverride(myCustomOverride);
// after: use a supported ReplaceOverride
beanDefinition.getMethodOverrides()
.addOverride(new ReplaceOverride("doWork", "workerReplacer")); Defensive patterns
Strategy: validation
Validate before calling
// Reject unsupported MethodOverride subclasses before AOT
for (MethodOverride o : beanDefinition.getMethodOverrides().getOverrides()) {
if (!(o instanceof LookupOverride) && !(o instanceof ReplaceOverride)) {
throw new IllegalStateException(
"Unsupported MethodOverride: " + o.getClass().getName());
}
} Type guard
// Narrow to supported override types boolean supported = override instanceof LookupOverride || override instanceof ReplaceOverride;
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Argument type mismatch: expected '
- Cannot apply @Lookup to beans without corresponding bean…
- Cannot invoke instance method without factoryBeanName:
- failed to generate code for bean definition
- Failed to instantiate method
AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09).
Data as JSON: /api/errors/6642fe31a2a0a0dd.
Report an issue: GitHub.
Appendix: source
Thrown at spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java:303
arguments.add(CodeBlock.of("$S", lookupOverride.getMethodName()));
arguments.add(CodeBlock.of("$S", lookupOverride.getBeanName()));
code.addStatement("$L.getMethodOverrides().addOverride(new $T($L))", BEAN_DEFINITION_VARIABLE,
LookupOverride.class, CodeBlock.join(arguments, ", "));
}
else if (methodOverride instanceof ReplaceOverride replaceOverride) {
Collection<CodeBlock> arguments = new ArrayList<>();
arguments.add(CodeBlock.of("$S", replaceOverride.getMethodName()));
arguments.add(CodeBlock.of("$S", replaceOverride.getMethodReplacerBeanName()));
List<String> typeIdentifiers = replaceOverride.getTypeIdentifiers();
if (!typeIdentifiers.isEmpty()) {
arguments.add(CodeBlock.of("java.util.List.of($S)",
StringUtils.collectionToDelimitedString(typeIdentifiers, ", ")));
}
code.addStatement("$L.getMethodOverrides().addOverride(new $T($L))", BEAN_DEFINITION_VARIABLE,
ReplaceOverride.class, CodeBlock.join(arguments, ", "));
}
else {
throw new UnsupportedOperationException("Unexpected MethodOverride subclass: " +
methodOverride.getClass().getName());
}
}
}
}
private CodeBlock generateValue(@Nullable String name, @Nullable Object value) {
PropertyNamesStack.push(name);
try {
return this.valueCodeGenerator.generateCode(value);
}
finally {
PropertyNamesStack.pop();
}
}
private Class<?> getInfrastructureType(RootBeanDefinition beanDefinition) {
if (beanDefinition.hasBeanClass()) {View on GitHub (pinned to 69bf83ad71)