mybatis/mybatis-3 · error · BuilderException
Error in result map '{resultMapId}'. Failed to find a constr
Error message
Error in result map '{resultMapId}'. Failed to find a constructor in '{resultType}' with arg names {constructorArgsByName}. Note that 'javaType' is required when there is ambiguous constructors or there is no writable property with the same name ('name' is optional, BTW). There is more info in the debug log. What it means
Thrown by ResultMappingConstructorResolver when automapping by constructor: all <constructor>/<arg> mappings have name attributes (constructorArgsByName fully populated) and no constructor of resultType could be matched after filtering candidates by parameter names and by supplied type info (javaType). The message notes that javaType is required when constructors are ambiguous or when arg names do not match writable properties.
Source
Thrown at src/main/java/org/apache/ibatis/builder/ResultMappingConstructorResolver.java:125
.filter(Objects::nonNull).collect(Collectors.toCollection(LinkedHashSet::new));
// arg order can only be 'fixed' if all mappings have property names
final boolean allMappingsHavePropertyNames = verifyPropertyNaming(constructorArgsByName);
// only do this if all property mappings were set
if (allMappingsHavePropertyNames) {
// while we have candidates, start selection
removeCandidatesBasedOnParameterNames(matchingConstructorCandidates, constructorArgsByName);
}
// resolve final constructor by filtering out selection based on type info present (or missing)
final ConstructorMetaInfo matchingConstructorInfo = filterBasedOnType(matchingConstructorCandidates,
constructorResultMappings, allMappingsHavePropertyNames);
if (matchingConstructorInfo == null) {
// [backwards-compatibility] (we cannot find a constructor),
// but this used to get thrown ONLY when property mappings have been set
if (allMappingsHavePropertyNames) {
throw new BuilderException("Error in result map '" + resultMapId + "'. Failed to find a constructor in '"
+ resultType.getName() + "' with arg names " + constructorArgsByName
+ ". Note that 'javaType' is required when there is ambiguous constructors or there is no writable property with the same name ('name' is optional, BTW). There is more info in the debug log.");
} else {
if (log.isDebugEnabled()) {
log.debug("Constructor for '" + resultMapId + "' could not be resolved.");
}
// return un-modified original mappings
return constructorResultMappings;
}
}
// only rebuild (auto-type) if required (any types are unidentified)
final boolean autoTypeRequired = constructorResultMappings.stream().map(ResultMapping::getJavaType)
.anyMatch(mappingType -> mappingType == null || Object.class.equals(mappingType));
final List<ResultMapping> resultMappings = autoTypeRequired
? autoTypeConstructorMappings(matchingConstructorInfo, constructorResultMappings, allMappingsHavePropertyNames)
: constructorResultMappings;
View on GitHub (pinned to 008069adb1)
Solutions
- Add explicit javaType (and jdbcType where needed) to each <arg>/<@Arg> so type-based filtering can pick the right constructor
- Make sure every arg name matches the constructor parameter name exactly (enable -parameters compiler flag for automatic param names)
- Verify a public constructor exists whose parameter count equals the number of constructor args
- Check the debug log (enable DEBUG for that logger) — it lists candidate constructors that were rejected and why
Example fix
<!-- before --> <constructor> <arg column="user_name" name="userName"/> </constructor> <!-- after --> <constructor> <arg column="user_name" name="userName" javaType="java.lang.String"/> </constructor>
Defensive patterns
Strategy: validation
Validate before calling
// Verify a matching constructor exists before relying on constructor automapping
static void assertConstructorMatch(Class<?> dto, List<String> argNames, List<Class<?>> argTypes) {
boolean ok = Arrays.stream(dto.getDeclaredConstructors()).anyMatch(c -> {
if (c.getParameterCount() != argNames.size()) return false;
Parameter[] params = c.getParameters();
for (int i = 0; i < params.length; i++) {
if (!params[i].getName().equals(argNames.get(i))) return false;
if (argTypes.get(i) != null && !params[i].getType().equals(argTypes.get(i))) return false;
}
return true;
});
if (!ok) throw new IllegalStateException("No constructor of " + dto.getName() + " matches args " + argNames);
} Prevention
- Compile with -parameters (javac) so constructor parameter names are visible to MyBatis
- Add explicit javaType to <arg>/@Arg when the DTO has multiple constructors
- Write a config-load test that executes one query per resultMap so constructor issues fail in CI
When it happens
Trigger: A <constructor> block whose arg name attributes don't match any actual constructor parameter names (e.g. names compiled without -parameters, or name mismatch), multiple constructors of the same arity with no javaType hints to disambiguate, or resultType lacking a matching-arity constructor.
Common situations: DTOs/records with multiple constructors; compiled code without the -parameters flag so reflection cannot read parameter names; renaming DTO fields without updating mapper constructor args; constructor arg count mismatch after adding a field.
Related errors
- Error in result map '{resultMapId}'. We do not support parti
- Do not know how to create an instance of " + resultType
- Cannot add a collection result to non-collection based resul
- There is no setter for property named '{}' in '{}'
- Error instantiating {} with invalid types ({}) or values ({}
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/083b303020d94fb4.
Report an issue: GitHub.