{"record":{"id":"c4d61754b91c3d2c","repo":"hibernate/hibernate-orm","slug":"could-not-locate-setter-method-for-property-s-o","errorCode":null,"errorMessage":"Could not locate setter method for property '%s' of class '%s'","messagePattern":"Could not locate setter method for property '(.+?)' of class '(.+?)'","errorType":"exception","errorClass":"PropertyNotFoundException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java","lineNumber":736,"sourceCode":"\t\tMethod potentialSetter = null;\n\n\t\tfor ( Method method : theClass.getDeclaredMethods() ) {\n\t\t\tfinal String methodName = method.getName();\n\t\t\tif ( method.getParameterCount() == 1 && methodName.equals( setterName ) ) {\n\t\t\t\tpotentialSetter = method;\n\t\t\t\tif ( propertyType == null || method.getParameterTypes()[0].equals( propertyType ) ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn potentialSetter;\n\t}\n\n\tpublic static Method findSetterMethod(final Class<?> containerClass, final String propertyName, final Class<?> propertyType) {\n\t\tfinal Method setter = setterMethodOrNull( containerClass, propertyName, propertyType );\n\t\tif ( setter == null ) {\n\t\t\tthrow new PropertyNotFoundException(\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\tLocale.ROOT,\n\t\t\t\t\t\t\t\"Could not locate setter method for property '%s' of class '%s'\",\n\t\t\t\t\t\t\tpropertyName,\n\t\t\t\t\t\t\tcontainerClass.getName()\n\t\t\t\t\t)\n\t\t\t);\n\t\t}\n\t\treturn setter;\n\t}\n\n\tprivate static Method setterOrNull(Class<?>[] interfaces, String propertyName, Class<?> propertyType, String likelyMethodName) {\n\t\tMethod setter = null;\n\t\tfor ( int i = 0; setter == null && i < interfaces.length; ++i ) {\n\t\t\tfinal var anInterface = interfaces[i];\n\t\t\tif ( !shouldSkipInterfaceCheck( anInterface ) ) {\n\t\t\t\tsetter = setterOrNull( anInterface, propertyName, propertyType, likelyMethodName );\n\t\t\t\tif ( setter == null ) {","sourceCodeStart":718,"sourceCodeEnd":754,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java#L718-L754","documentation":"Property access needs a writer as well as a reader. ReflectHelper.findSetterMethod searches the class hierarchy and interfaces for a public setFoo(...) whose single parameter equals the property type; when no method matches both the name and the parameter type, it throws PropertyNotFoundException. A setter taking a different type than the property is rejected exactly like a missing one.","triggerScenarios":"SessionFactory bootstrap or runtime property writes on a property-access mapping where the mapped property has no public setter, or where the only setter's parameter type differs from the property/field type. Direct calls to ReflectHelper.findSetterMethod with such a class/property pair hit the same path.","commonSituations":"Immutable or builder-style classes without setters; setter parameter type widened or narrowed during a refactor; package-private setters; @Embeddable components lacking setters; switching a class from field access to default property access without adding writers.","solutions":["Add a public setter setFoo(T) whose parameter type exactly matches the property/field type.","Annotate the class or property with @Access(AccessType.FIELD) so Hibernate writes the field directly.","For immutable values, map them as records/@Embeddable populated through the constructor instead of setters.","If a setter already exists, make it public and align its parameter type with the mapped property type."],"exampleFix":"// before: default property access, no setter\n@Entity\npublic class Account {\n    @Id private Long id;\n    private BigDecimal balance; // no setBalance -> PropertyNotFoundException\n}\n\n// after: field access, no setter required\n@Entity\n@Access(AccessType.FIELD)\npublic class Account {\n    @Id private Long id;\n    private BigDecimal balance;\n}","handlingStrategy":"validation","validationCode":"static void verifySetters(Class<?> entityClass, List<String> mappedPropertyNames) {\n    try {\n        java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(entityClass);\n        java.util.Set<String> writable = new java.util.HashSet<>();\n        for (java.beans.PropertyDescriptor pd : info.getPropertyDescriptors()) {\n            if (pd.getWriteMethod() != null) writable.add(pd.getName());\n        }\n        for (String name : mappedPropertyNames) {\n            if (!writable.contains(name)) {\n                throw new IllegalStateException(\"No setter for mapped property '\" + name + \"' on \" + entityClass.getName());\n            }\n        }\n    } catch (java.beans.IntrospectionException e) {\n        throw new IllegalStateException(\"Cannot introspect \" + entityClass.getName(), e);\n    }\n}","typeGuard":"static boolean hasSetter(Class<?> clazz, String property) {\n    try {\n        return new java.beans.PropertyDescriptor(property, clazz).getWriteMethod() != null;\n    } catch (java.beans.IntrospectionException e) {\n        return false;\n    }\n}","tryCatchPattern":"try {\n    SessionFactory sf = metadata.buildSessionFactory();\n} catch (org.hibernate.PropertyNotFoundException e) {\n    // message names the property and class; add the setter or switch to @Access(AccessType.FIELD)\n}","preventionTips":["Use @Access(AccessType.FIELD) on entity classes to decouple mapping from accessor presence.","Keep setter parameter types identical to the corresponding field types.","Build the SessionFactory in CI to catch accessor gaps early."],"tags":["hibernate","reflection","mapping","javabean","property-access"],"backgroundTag":"missing-java-bean-setter","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}