mybatis/mybatis-3 · error · BuilderException
Ambiguous collection type for property '{property}'. You mus
Error message
Ambiguous collection type for property '{property}'. You must specify 'javaType' or 'resultMap'. What it means
Thrown by XMLMapperBuilder.validateCollection() when a <collection> element in a resultMap has neither resultMap nor javaType, and the enclosing result type has no setter for the given property. MyBatis can usually infer the collection's element type from a setter's generic parameter; without a setter to inspect and without an explicit type, the mapping is ambiguous and rejected at build time.
Source
Thrown at src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java:395
private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings,
Class<?> enclosingType) {
if (Arrays.asList("association", "collection", "case").contains(context.getName())
&& context.getStringAttribute("select") == null) {
validateCollection(context, enclosingType);
ResultMap resultMap = resultMapElement(context, resultMappings, enclosingType);
return resultMap.getId();
}
return null;
}
protected void validateCollection(XNode context, Class<?> enclosingType) {
if ("collection".equals(context.getName()) && context.getStringAttribute("resultMap") == null
&& context.getStringAttribute("javaType") == null) {
MetaClass metaResultType = MetaClass.forClass(enclosingType, configuration.getReflectorFactory());
String property = context.getStringAttribute("property");
if (!metaResultType.hasSetter(property)) {
throw new BuilderException(
"Ambiguous collection type for property '" + property + "'. You must specify 'javaType' or 'resultMap'.");
}
}
}
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
// ignore, bound type is not required
}
if (boundType != null && !configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResourceView on GitHub (pinned to 008069adb1)
Solutions
- Add javaType="java.util.List" (or Set/Collection) to the <collection> so the type no longer needs inference
- Or add a resultMap="itemResultMap" attribute pointing at an explicit resultMap for the element type
- Or give the enclosing type a standard setter setItems(java.util.List<Item> item) / fix the typo in the property attribute
Example fix
<!-- before --> <resultMap id="orderRM" type="Order"> <collection property="items" ofType="Item"/> </resultMap> <!-- after --> <resultMap id="orderRM" type="Order"> <collection property="items" javaType="java.util.List" ofType="Item"/> </resultMap>
Defensive patterns
Strategy: validation
Validate before calling
// static check before running: collection needs javaType/resultMap OR a real setter
boolean hasSetter = MetaClass.forClass(enclosingType, new DefaultReflectorFactory()).hasSetter(property);
if (!hasSetter && javaType == null && resultMap == null)
throw new IllegalStateException("<collection property='" + property + "'> is ambiguous"); Prevention
- Always set javaType (or resultMap) on <collection> when the enclosing type lacks classic setters
- Be explicit with Lombok @Builder/record result types
- Smoke-build the configuration in CI
When it happens
Trigger: <collection property="items" ofType="Item"/> inside a <resultMap type="Order"> where Order has no setItems(...) method (wrong property name, field-only class, immutable class, or Lombok @Builder without setters). Absent only when both the resultMap and javaType attributes of <collection> are null.
Common situations: Using Lombok @Builder/@Value or Java records as result types (no classic setters); typo in the property attribute; mapping onto a third-party DTO with fluent setters (setX returning 'this') that the reflector does not recognize; nested collections refactored to use ofType only.
Related errors
- Mapping is missing column attribute for property {}
- Could not find a parent resultMap with id '{extend}'
- Could not find result map '{resultMapName}' referenced from
- Error in result map '{resultMapId}'. Failed to find a constr
- Error in result map '{resultMapId}'. We do not support parti
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/c9158c4e619cec07.
Report an issue: GitHub.