spring-projects/spring-framework · error · FatalBeanException
Failed to obtain BeanInfo for class [${beanClass.getName()}]
Error message
Failed to obtain BeanInfo for class [${beanClass.getName()}] What it means
FatalBeanException thrown by CachedIntrospectionResults when java.beans.Introspector.getBeanInfo(beanClass) (or the surrounding introspection loop) raises IntrospectionException. It means the JDK's bean introspection refused to process the class at all - the class could not be analysed for property descriptors, blocking any BeanWrapper / copyProperties / DataBinder usage of it.
Source
Thrown at spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java:299
readMethodNames.add(readMethod.getName());
}
}
// Explicitly check implemented interfaces for setter/getter methods as well,
// in particular for interface default methods.
Class<?> currClass = beanClass;
while (currClass != null && currClass != Object.class) {
introspectInterfaces(beanClass, currClass, readMethodNames);
currClass = currClass.getSuperclass();
}
// Check for record-style accessors without prefix: for example, "lastName()"
// - accessor method directly referring to instance field of same name
// - same convention for component accessors of Java 15 record classes
introspectPlainAccessors(beanClass, readMethodNames);
}
catch (IntrospectionException ex) {
throw new FatalBeanException("Failed to obtain BeanInfo for class [" + beanClass.getName() + "]", ex);
}
}
private void introspectInterfaces(Class<?> beanClass, Class<?> currClass, Set<String> readMethodNames)
throws IntrospectionException {
for (Class<?> ifc : currClass.getInterfaces()) {
if (!ClassUtils.isJavaLanguageInterface(ifc)) {
for (PropertyDescriptor pd : getBeanInfo(ifc).getPropertyDescriptors()) {
PropertyDescriptor existingPd = this.propertyDescriptors.get(pd.getName());
if (existingPd == null ||
(existingPd.getReadMethod() == null && pd.getReadMethod() != null)) {
// GenericTypeAwarePropertyDescriptor leniently resolves a set* write method
// against a declared read method, so we prefer read method descriptors here.
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
if (pd.getWriteMethod() == null &&
isInvalidReadOnlyPropertyType(pd.getPropertyType(), beanClass)) {
// Ignore read-only properties such as ClassLoader - no need to bind to thoseView on GitHub (pinned to e8729d0438)
Solutions
- Inspect the attached IntrospectionException cause for which method/class pair triggered it.
- Align the conflicting getter/setter so their types are mutually assignable.
- Avoid exposing the problematic class to Spring's BeanWrapper; map it manually or via a converter.
- If generated bytecode, regenerate with a stable tool version and verify with java.beans.Introspector.getBeanInfo in a standalone test.
Example fix
// reproducer
java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(MyClass.class);
// before: setItems(List<String>) getItems() returns Collection -> may conflict
// after: align types
public List<String> getItems() { ... }
public void setItems(List<String> items) { ... } Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: ask the JDK Introspector before Spring wraps it
try {
java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(MyClass.class);
// exercise descriptors to surface latent IntrospectionException
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
pd.getReadMethod(); pd.getWriteMethod();
}
} catch (java.beans.IntrospectionException e) {
throw new IllegalStateException("Class cannot be introspected; fix accessors", e);
} Type guard
static boolean isIntrospectable(Class<?> c) {
try { java.beans.Introspector.getBeanInfo(c); return true; }
catch (java.beans.IntrospectionException e) { return false; }
} Try / catch
try {
return CachedIntrospectionResults.forClass(beanClass);
} catch (FatalBeanException ex) {
if (ex.getCause() instanceof java.beans.IntrospectionException) {
// log offending class and switch to manual mapping
log.error("Cannot introspect {}", beanClass.getName(), ex.getCause());
}
throw ex;
} Prevention
- Keep bean accessors standard (get/set with consistent types).
- Unit-test Introspector.getBeanInfo for any class exposed to BeanWrapper.
- Watch generated/proxied classes - validate their introspection in isolation.
- Avoid method overloads that confuse the Introspector.
When it happens
Trigger: First access of a class through CachedIntrospectionResults.forClass(...) when the class has a malformed PropertyDescriptor pair the Introspector cannot reconcile (e.g. a setter whose parameter type is incompatible with the getter return in a way the Introspector flags), or getBeanInfo fails on a classloader/security issue.
Common situations: A class with conflicting getter/setter signatures introduced by a refactor; a class generated by a bytecode tool producing signatures the Introspector rejects; rare on hand-written POJOs, more common on generated/proxied classes with non-standard accessor methods.
Related errors
- Failed to re-introspect class [${beanClass.getName()}]
- Bean property '{propertyName}' is not readable or has an inv
- No property '${propertyName}' found
- Write method must have exactly 1 or 2 parameters: ${method}
- Bad read method arg count: ${readMethod}
AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04).
Data as JSON: /data/errors/4aa9f9b4b44bda84.json.
Report an issue: GitHub.