{"record":{"id":"de96f3e09ef95e2c","repo":"spring-projects/spring-ai","slug":"single-parameter-must-be-of-type-progressnotificat","errorCode":null,"errorMessage":"Single parameter must be of type ProgressNotification: {method.getName()} in {method.getDeclaringClass().getName()} has parameter of type {parameters[0].getType().getName()}","messagePattern":"Single parameter must be of type ProgressNotification: (.+?) in (.+?) has parameter of type (.+?)","errorType":"validation","errorClass":"java.lang.IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/progress/AbstractMcpProgressMethodCallback.java","lineNumber":103,"sourceCode":"\t * @param method The method to validate\n\t * @throws IllegalArgumentException if the parameters are not compatible\n\t */\n\tprotected void validateParameters(Method method) {\n\t\tParameter[] parameters = method.getParameters();\n\n\t\t// Check parameter count - must have either 1 or 3 parameters\n\t\tif (parameters.length != 1 && parameters.length != 3) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Method must have either 1 parameter (ProgressNotification) or 3 parameters (Double, String, String): \"\n\t\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName() + \" has \"\n\t\t\t\t\t\t\t+ parameters.length + \" parameters\");\n\t\t}\n\n\t\t// Check parameter types\n\t\tif (parameters.length == 1) {\n\t\t\t// Single parameter must be ProgressNotification\n\t\t\tif (!ProgressNotification.class.isAssignableFrom(parameters[0].getType())) {\n\t\t\t\tthrow new IllegalArgumentException(\"Single parameter must be of type ProgressNotification: \"\n\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName() + \" has parameter of type \"\n\t\t\t\t\t\t+ parameters[0].getType().getName());\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// Three parameters must be Double, String, String\n\t\t\tif (!Double.class.isAssignableFrom(parameters[0].getType())\n\t\t\t\t\t&& !double.class.isAssignableFrom(parameters[0].getType())) {\n\t\t\t\tthrow new IllegalArgumentException(\"First parameter must be of type Double or double: \"\n\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName() + \" has parameter of type \"\n\t\t\t\t\t\t+ parameters[0].getType().getName());\n\t\t\t}\n\t\t\tif (!String.class.isAssignableFrom(parameters[1].getType())) {\n\t\t\t\tthrow new IllegalArgumentException(\"Second parameter must be of type String: \" + method.getName()\n\t\t\t\t\t\t+ \" in \" + method.getDeclaringClass().getName() + \" has parameter of type \"\n\t\t\t\t\t\t+ parameters[1].getType().getName());\n\t\t\t}\n\t\t\tif (!String.class.isAssignableFrom(parameters[2].getType())) {","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/progress/AbstractMcpProgressMethodCallback.java#L85-L121","documentation":"For a single-parameter progress handler, the parameter must be assignable from ProgressNotification. A one-arg handler whose parameter is any other type (String, Double, a custom DTO) fails validation at registration with this IllegalArgumentException. The message template's {…} placeholders are literal; the offending type name is appended after the colon.","triggerScenarios":"Registering onProgress(String token) or onProgress(MyCustomNotification n) as a single-parameter progress handler; validateParameters reaches the single-parameter branch and the isAssignableFrom check fails.","commonSituations":"Confusing the progress handler signature with the three-parameter (Double, String, String) variant; using a project-specific notification type instead of io.modelcontextprotocol.spec.ProgressNotification; auto-complete picking the wrong ProgressNotification import from another package.","solutions":["Change the single parameter's type to ProgressNotification.","Alternatively use the three-parameter form (Double, String, String) if you want primitive progress fields.","Verify the import — use the MCP SDK's ProgressNotification, not a same-named class from another library.","If you need richer data, wrap your custom payload into the handler body rather than the signature."],"exampleFix":"// before\npublic void onProgress(String progressToken) { ... }\n// after\npublic void onProgress(ProgressNotification notification) {\n    String token = notification.progressToken();\n    ...\n}","handlingStrategy":"validation","validationCode":"Parameter[] ps = handlerMethod.getParameters();\nif (ps.length == 1 && !ProgressNotification.class.isAssignableFrom(ps[0].getType())) {\n    throw new IllegalStateException(\"Single progress handler param must be ProgressNotification: \" + handlerMethod);\n}","typeGuard":null,"tryCatchPattern":"try {\n    registry.registerProgress(bean, method);\n} catch (IllegalArgumentException e) {\n    logger.error(\"Invalid progress handler parameter type: {}\", e.getMessage());\n}","preventionTips":["Import ProgressNotification from the MCP SDK, not a similarly named class","Use the three-param (Double, String, String) form if you want primitive fields","Run registration tests in CI before deployment"],"tags":["mcp","validation","method-signature","type-mismatch","java"],"backgroundTag":"type-mismatch","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}