mybatis/mybatis-3 · error · ExecutorException
Constructor auto-mapping of ''{0}'' failed. The constructor
Error message
Constructor auto-mapping of ''{0}'' failed. The constructor takes ''{1}'' arguments, but there are only ''{2}'' columns in the result set. What it means
Thrown during column-order-based constructor auto-mapping when the chosen constructor's parameter count exceeds the number of columns in the result set. MyBatis maps constructor arguments positionally (column i -> parameter i), so if the constructor needs more arguments than there are columns, mapping is impossible and fails fast with the constructor, its arity, and the column count.
Source
Thrown at src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java:915
throws SQLException {
boolean foundValues = false;
if (configuration.isArgNameBasedConstructorAutoMapping()) {
foundValues = applyArgNameBasedConstructorAutoMapping(rsw, resultMap, columnPrefix, constructorArgTypes,
constructorArgs, constructor, foundValues);
} else {
foundValues = applyColumnOrderBasedConstructorAutomapping(rsw, constructorArgTypes, constructorArgs, constructor,
foundValues);
}
return foundValues || configuration.isReturnInstanceForEmptyRow()
? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
}
private boolean applyColumnOrderBasedConstructorAutomapping(ResultSetWrapper rsw, List<Class<?>> constructorArgTypes,
List<Object> constructorArgs, Constructor<?> constructor, boolean foundValues) throws SQLException {
Class<?>[] parameterTypes = constructor.getParameterTypes();
if (parameterTypes.length > rsw.getClassNames().size()) {
throw new ExecutorException(MessageFormat.format(
"Constructor auto-mapping of ''{0}'' failed. The constructor takes ''{1}'' arguments, but there are only ''{2}'' columns in the result set.",
constructor, parameterTypes.length, rsw.getClassNames().size()));
}
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
String columnName = rsw.getColumnNames().get(i);
TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
constructorArgTypes.add(parameterType);
constructorArgs.add(value);
foundValues = value != null || foundValues;
}
return foundValues;
}
private boolean applyArgNameBasedConstructorAutoMapping(ResultSetWrapper rsw, ResultMap resultMap,
String columnPrefix, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, Constructor<?> constructor,View on GitHub (pinned to 008069adb1)
Solutions
- Add the missing columns to the SELECT clause so the result set has at least as many columns as the constructor takes.
- Switch to a constructor whose parameter count matches the result set columns (or use @AutomapConstructor on the right one).
- Remove unused parameters from the constructor, or supply them via <arg> with a literal/value rather than a column.
- If columns exist but names do not line up positionally, use argNameBasedConstructorAutoMapping with -parameters so matching is by name, and ensure column names match parameter names (mind mapUnderscoreToCamelCase).
Example fix
// before
public User(Long id, String name, String email) { ... }
// query: SELECT id, name FROM users
// after
public User(Long id, String name, String email) { ... }
// query: SELECT id, name, email FROM users Defensive patterns
Strategy: validation
Validate before calling
// before opening the cursor you cannot count columns; validate after query setup
try (ResultSet dummy = statement.executeQuery("SELECT * FROM (" + sql + ") q WHERE 1=0")) {
int columnCount = dummy.getMetaData().getColumnCount();
if (columnCount < ctor.getParameterCount()) {
throw new IllegalStateException("Query returns " + columnCount
+ " columns but constructor needs " + ctor.getParameterCount());
}
} Try / catch
catch (ExecutorException e) when e.getMessage().contains("Constructor auto-mapping") && e.getMessage().contains("columns in the result set") -> log column list vs constructor signature and fail the surrounding test/call explicitly. Prevention
- Keep SELECT column lists explicit (no SELECT *) so constructor arity and columns evolve together.
- Add an integration test per mapped query asserting the DTO deserializes, catching arity drift on schema change.
When it happens
Trigger: A result map or auto-mapping selects a constructor with N parameters while the executed query returns fewer than N columns (SELECT with fewer columns than the constructor needs, or a constructor with extra parameters). Happens on the non-argNameBased path where a constructor was selected by type/arity via findUsableConstructorByArgTypes, or with @AutomapConstructor on an oversized constructor.
Common situations: Adding a new field/parameter to a constructor without updating the SELECT column list; using SELECT * against a view that dropped columns; explicit <constructor> arg entries exceeding returned columns; joining to a projection with fewer columns than the DTO constructor expects.
Related errors
- 'argNameBasedConstructorAutoMapping' is enabled and the clas
- Constructor auto-mapping of ''{1}'' failed because ''{0}'' w
- Could not commit transaction. Cause: {}
- Line missing end-of-line terminator ({}) => {}
- Statement returned {} results where exactly one (1) was expe
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/d2c3179d45f3e85f.
Report an issue: GitHub.