mybatis/mybatis-3 · error · ExecutorException
Nested lazy loaded result property '" + property + "' for…
Error message
Nested lazy loaded result property '" + property + "' for query id '" + resultLoader.mappedStatement.getId() + " already exists in the result map. The leftmost property of all lazy loaded properties must be unique within a result map."
What it means
ResultLoaderMap keys pending lazy loads by the LEFTMOST segment of the property path (order.customer -> 'order'). Two lazy properties in one resultMap that share the same leftmost segment would fight over the same loader slot and overwrite each other, so addLoader rejects the second one with this ExecutorException. Note the message contains a known typo ('query id ... already exists' string concatenation).
Solutions
- Give the two lazy properties distinct leftmost segments, e.g. property="pendingOrders" and property="shippedOrders"
- Combine them into a single nested select that populates the shared parent in one query
Example fix
<!-- before --> <resultMap id="m" type="T"> <association property="orders.pending" select="selPending" fetchType="lazy"/> <association property="orders.shipped" select="selShipped" fetchType="lazy"/> </resultMap> <!-- after --> <resultMap id="m" type="T"> <association property="pendingOrders" select="selPending" fetchType="lazy"/> <association property="shippedOrders" select="selShipped" fetchType="lazy"/> </resultMap>
Defensive patterns
Strategy: validation
Validate before calling
// Lint resultMaps: no two lazy properties may share a leftmost segment
Set<String> leftmost = new HashSet<>();
for (String prop : lazyPropertyPaths) {
String head = prop.split("\\.")[0];
if (!leftmost.add(head)) throw new IllegalStateException("duplicate leftmost lazy property: " + head);
} Prevention
- Keep one nested select per top-level lazy property in each resultMap
- Review resultMaps after splitting associations into sub-properties
When it happens
Trigger: A resultMap declaring two nested selects whose property paths start with the same segment, e.g. property="orders.pending" and property="orders.shipped" — both map to key 'orders'.
Common situations: Splitting one association into multiple lazy sub-properties under the same parent name; refactoring resultMaps during entity redesign.
Related errors
- A query was run and no Result Maps were found for the…
- Ambiguous collection type for property
- An attempt has been made to read a not loaded lazy property…
- Cannot add a collection result to non-collection based…
- Cannot enable lazy loading because CGLIB is not available…
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/3cbe59de6a92cd6e.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/org/apache/ibatis/executor/loader/ResultLoaderMap.java:55
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
/**
* @author Clinton Begin
* @author Franta Mejta
*/
public class ResultLoaderMap {
private final Map<String, LoadPair> loaderMap = new HashMap<>();
public void addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
String upperFirst = getUppercaseFirstProperty(property);
if (!upperFirst.equalsIgnoreCase(property) && loaderMap.containsKey(upperFirst)) {
throw new ExecutorException("Nested lazy loaded result property '" + property + "' for query id '"
+ resultLoader.mappedStatement.getId()
+ " already exists in the result map. The leftmost property of all lazy loaded properties must be unique within a result map.");
}
loaderMap.put(upperFirst, new LoadPair(property, metaResultObject, resultLoader));
}
public final Map<String, LoadPair> getProperties() {
return new HashMap<>(this.loaderMap);
}
public Set<String> getPropertyNames() {
return loaderMap.keySet();
}
public int size() {
return loaderMap.size();
}
View on GitHub (pinned to 008069adb1)