{"record":{"id":"9d77d6779d5f13fb","repo":"mybatis/mybatis-3","slug":"could-not-find-parameter-paramname-note-that","errorCode":null,"errorMessage":"Could not find parameter '${paramName}'. Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). Specified key properties are ${keyProperties} and available parameters are ${keySet}","messagePattern":"Could not find parameter '(.+?)'\\. Note that when there are multiple parameters, 'keyProperty' must include the parameter name \\(e\\.g\\. 'param\\.id'\\)\\. Specified key properties are (.+?) and available parameters are (.+?)","errorType":"exception","errorClass":"ExecutorException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java","lineNumber":207,"sourceCode":"    if (firstDot == -1) {\n      if (singleParam) {\n        return getAssignerForSingleParam(config, rsmd, columnPosition, paramMap, keyProperty, omitParamName);\n      }\n      throw new ExecutorException(\"Could not determine which parameter to assign generated keys to. \"\n          + \"Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). \"\n          + \"Specified key properties are \" + ArrayUtil.toString(keyProperties) + \" and available parameters are \"\n          + keySet);\n    }\n    String paramName = keyProperty.substring(0, firstDot);\n    if (keySet.contains(paramName)) {\n      String argParamName = omitParamName ? null : paramName;\n      String argKeyProperty = keyProperty.substring(firstDot + 1);\n      return Map.entry(paramName, new KeyAssigner(config, rsmd, columnPosition, argParamName, argKeyProperty));\n    }\n    if (singleParam) {\n      return getAssignerForSingleParam(config, rsmd, columnPosition, paramMap, keyProperty, omitParamName);\n    } else {\n      throw new ExecutorException(\"Could not find parameter '\" + paramName + \"'. \"\n          + \"Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). \"\n          + \"Specified key properties are \" + ArrayUtil.toString(keyProperties) + \" and available parameters are \"\n          + keySet);\n    }\n  }\n\n  private Entry<String, KeyAssigner> getAssignerForSingleParam(Configuration config, ResultSetMetaData rsmd,\n      int columnPosition, Map<String, ?> paramMap, String keyProperty, boolean omitParamName) {\n    // Assume 'keyProperty' to be a property of the single param.\n    String singleParamName = nameOfSingleParam(paramMap);\n    String argParamName = omitParamName ? null : singleParamName;\n    return Map.entry(singleParamName, new KeyAssigner(config, rsmd, columnPosition, argParamName, keyProperty));\n  }\n\n  private static String nameOfSingleParam(Map<String, ?> paramMap) {\n    // There is virtually one parameter, so any key works.\n    return paramMap.keySet().iterator().next();\n  }","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java#L189-L225","documentation":"When keyProperty contains a parameter prefix (e.g. 'user.id'), MyBatis splits it at the first dot and looks up 'user' among the available parameter names. If that name is not in the ParamMap and there is more than one parameter, the target parameter simply does not exist, so this ExecutorException (echoing the missing name, the specified keyProperties, and the available parameters) is thrown.","triggerScenarios":"keyProperty=\"item.id\" when the @Param is named differently (e.g. @Param(\"user\") or no annotation so MyBatis used arg0/param1); renaming a @Param annotation without updating keyProperty; typos in the prefix.","commonSituations":"Refactoring parameter names, mixing positional names (arg0, param1) with custom @Param names, copying an XML statement from another mapper with different parameter names.","solutions":["Make the keyProperty prefix exactly match the @Param name (or the default param1/arg0 name shown in 'available parameters')","Add the missing @Param annotation so the name exists","Copy the correct name from the error message's 'available parameters are [...]' list"],"exampleFix":"<!-- before: @Param(\"user\") but keyProperty uses 'item' -->\n<insert ... keyProperty=\"item.id\">\n\n<!-- after -->\n<insert ... keyProperty=\"user.id\">","handlingStrategy":"validation","validationCode":"// Verify the prefix matches a declared @Param before first use\nString prefix = keyProperty.split(\"\\\\.\")[0];\nboolean ok = Arrays.stream(method.getParameters()).anyMatch(p -> p.isAnnotationPresent(Param.class) && p.getAnnotation(Param.class).value().equals(prefix));\nif (!ok) throw new IllegalStateException(\"keyProperty prefix '\" + prefix + \"' matches no @Param\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["The error message lists available parameter names — copy the name from it verbatim","Rename @Param and keyProperty in the same commit"],"tags":["generated-keys","keyproperty","param-name"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}