mybatis/mybatis-3 · error · BindingException
Parameter '{key}' not found. Available parameters are {keySe
Error message
Parameter '{key}' not found. Available parameters are {keySet} What it means
ParamMap.get() is the strict parameter map MyBatis uses for named parameter lookup in SQL (#{...}). Unlike a plain HashMap it throws when the key is absent, so typos in a #{} placeholder or misuse of @Param names produce this precise diagnostic listing the actually available keys.
Source
Thrown at src/main/java/org/apache/ibatis/binding/MapperMethod.java:210
Map<K, V> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), rowBounds);
} else {
result = sqlSession.selectMap(command.getName(), param, method.getMapKey());
}
return result;
}
public static class ParamMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -2212268410512043556L;
@Override
public V get(Object key) {
if (!super.containsKey(key)) {
throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet());
}
return super.get(key);
}
}
public static class SqlCommand {
private final String name;
private final SqlCommandType type;
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) == null) {
throw new BindingException(View on GitHub (pinned to 008069adb1)
Solutions
- Read the 'Available parameters are [...]' list in the message and use one of those names in #{}
- Add or correct @Param("...") on the mapper method parameter to match the placeholder
- For multiple parameters, annotate every parameter with @Param explicitly
Example fix
<!-- before -->
User find(@Param("id") Long id);
SELECT * FROM users WHERE user_id = #{userId}
<!-- after -->
User find(@Param("userId") Long id);
SELECT * FROM users WHERE user_id = #{userId} Defensive patterns
Strategy: validation
Validate before calling
Object[] args = ...; // before calling // with @Param names known, verify placeholders exist in the ParamMap once in tests: // MapperMethod.ParamMap#get throws; guard by checking containsKey in a custom wrapper if needed.
Try / catch
catch (PersistenceException e) { Throwable c = e.getCause(); if (c instanceof BindingException && c.getMessage().contains("not found. Available parameters")) { /* surface available list to developer */ } throw e; } Prevention
- Annotate every multi-parameter mapper method with @Param and mirror names in #{}
- Add a CI test that parses each mapper XML against its interface (mybatis catches drift at first call otherwise)
- Prefer consistent naming: parameter name = SQL placeholder name
When it happens
Trigger: SQL contains #{userId} but the method declares @Param("id") or no @Param at all; referencing #{param1} style names when actual names differ; OGNL navigation on a property that does not exist on the single POJO parameter (e.g. #{user.name} when the parameter has no 'user' field); a nested association referencing a property not present in the row object.
Common situations: Renaming a Java parameter or @Param without updating the XML; copy-pasting SQL between mappers with different parameter names; multiple parameters without @Param annotations (available names become arg0/param1 style).
Related errors
- {methodName} cannot have multiple {paramType} parameters
- Unknown execution method for: {name}
- Mapper method '{name}' attempted to return null from a metho
- Mapper method '{name}' has an unsupported return type: {retu
- method {name} needs either a @ResultMap annotation, a @Resul
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/7f8e3094509babf5.
Report an issue: GitHub.