{"record":{"id":"dacf988991c57b99","repo":"spring-projects/spring-ai","slug":"method-must-have-either-1-parameter-progressnotif","errorCode":null,"errorMessage":"Method must have either 1 parameter (ProgressNotification) or 3 parameters (Double, String, String): {method.getName()} in {method.getDeclaringClass().getName()} has {parameters.length} parameters","messagePattern":"Method must have either 1 parameter \\(ProgressNotification\\) or 3 parameters \\(Double, String, String\\): (.+?) in (.+?) has (.+?) parameters","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":93,"sourceCode":"\t * This method should be implemented by subclasses to handle specific return type\n\t * validation.\n\t * @param method The method to validate\n\t * @throws IllegalArgumentException if the return type is not compatible\n\t */\n\tprotected abstract void validateReturnType(Method method);\n\n\t/**\n\t * Validates method parameters. This method provides common validation logic and\n\t * delegates exchange type checking to subclasses.\n\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())) {","sourceCodeStart":75,"sourceCodeEnd":111,"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#L75-L111","documentation":"Progress handler methods must take exactly one parameter (a ProgressNotification) or exactly three parameters (Double progress, String total/progressToken, String message). Any other parameter count is rejected at registration with this IllegalArgumentException. Note the message template uses literal {…} placeholders that are not interpolated — the actual counts are appended after the colon.","triggerScenarios":"Annotating a progress handler with 0, 2, 4+ parameters (e.g. onProgress(ProgressNotification n, String extra)) and registering it; constructor-time validateMethod -> validateParameters throws.","commonSituations":"Adding a context or session parameter to a handler signature; migrating from a three-arg style to the notification-object style but keeping a leftover second parameter; copy-paste from other MCP callback types with different arity rules.","solutions":["Reduce or expand the signature to exactly (ProgressNotification) or (Double, String, String).","If you need extra context, capture it via constructor/field injection into the bean instead of method parameters.","Check the parameter order for the three-arg form matches (progress, total, progressToken/message) per the class docs.","Read the appended text after the colon in the message to see the actual offending parameter count."],"exampleFix":"// before\npublic void onProgress(ProgressNotification n, McpSession session) { ... }\n// after\npublic void onProgress(ProgressNotification n) { ... } // session held as a bean field","handlingStrategy":"validation","validationCode":"int n = handlerMethod.getParameterCount();\nif (n != 1 && n != 3) {\n    throw new IllegalStateException(\"Progress handler must have 1 (ProgressNotification) or 3 (Double,String,String) params: \" + handlerMethod);\n}","typeGuard":null,"tryCatchPattern":"try {\n    registry.registerProgress(bean, method);\n} catch (IllegalArgumentException e) {\n    logger.error(\"Invalid progress handler arity: {}\", e.getMessage());\n}","preventionTips":["Memorize the two allowed signatures: (ProgressNotification) or (Double, String, String)","Pass extra context via bean fields, not method parameters","Registration smoke tests catch arity errors at CI time"],"tags":["mcp","validation","method-signature","java"],"backgroundTag":"invalid-argument-format","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"}