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

  1. Read the 'Available parameters are [...]' list in the message and use one of those names in #{}
  2. Add or correct @Param("...") on the mapper method parameter to match the placeholder
  3. 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

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


AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14). Data as JSON: /api/errors/7f8e3094509babf5. Report an issue: GitHub.