{"record":{"id":"8492abd4d15c98b3","repo":"spring-projects/spring-ai","slug":"method-must-have-at-least-1-parameter-createmessa","errorCode":null,"errorMessage":"Method must have at least 1 parameter (CreateMessageRequest): {methodName} in {className} has {paramCount} parameters","messagePattern":"Method must have at least 1 parameter \\(CreateMessageRequest\\): (.+?) in (.+?) has (.+?) parameters","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/sampling/AbstractMcpSamplingMethodCallback.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 at least 1 parameter\n\t\tif (parameters.length < 1) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Method must have at least 1 parameter (CreateMessageRequest): \" + method.getName() + \" in \"\n\t\t\t\t\t\t\t+ method.getDeclaringClass().getName() + \" has \" + 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 CreateMessageRequest\n\t\t\tif (!CreateMessageRequest.class.isAssignableFrom(parameters[0].getType())) {\n\t\t\t\tthrow new IllegalArgumentException(\"Single parameter must be of type CreateMessageRequest: \"\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// TODO: Support for multiple parameters corresponding to CreateMessageRequest\n\t\t\t// fields\n\t\t\t// For now, we only support the single parameter version\n\t\t\tthrow new IllegalArgumentException(","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/sampling/AbstractMcpSamplingMethodCallback.java#L75-L111","documentation":"This error means a method registered as an MCP sampling callback takes no parameters, but the framework requires at least one. The sampling protocol invokes the callback with a CreateMessageRequest, so the callback method must accept it as its (first) argument. AbstractMcpSamplingMethodCallback.validateParameters() enforces this at registration time and throws IllegalArgumentException.","triggerScenarios":"Registering a @McpSampling (sampling) handler method whose signature has zero parameters, e.g. `void mySampler()` annotated for sampling; the validation runs in validateMethod during callback construction.","commonSituations":"Developers copy an unannotated helper method and add the sampling annotation without changing its signature; they assume the request is optional or injected elsewhere; refactoring removed the parameter but left the annotation in place.","solutions":["Add a single parameter of type io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest to the annotated method","If the method was not meant to be a sampling handler, remove the sampling annotation from it","Rebuild/re-register the MCP server and confirm the callback validates successfully"],"exampleFix":"// before\n@McpSampling\npublic String sample() { return \"reply\"; }\n// after\n@McpSampling\npublic String sample(CreateMessageRequest request) { return \"reply: \" + request.messages(); }","handlingStrategy":"validation","validationCode":"for (Method m : clazz.getDeclaredMethods()) {\n    if (m.isAnnotationPresent(McpSampling.class)\n            && m.getParameterCount() < 1) {\n        throw new IllegalStateException(\"Sampling method \" + m.getName()\n            + \" must declare a CreateMessageRequest parameter\");\n    }\n}","typeGuard":"boolean isValidSamplingMethod(Method m) {\n    return m.getParameterCount() >= 1\n        && CreateMessageRequest.class.isAssignableFrom(m.getParameterTypes()[0]);\n}","tryCatchPattern":"try {\n    registerSamplingCallback(method);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"at least 1 parameter\")) {\n        log.error(\"Bad sampling signature on {}: {}\", method, e.getMessage());\n    } else { throw e; }\n}","preventionTips":["Always declare sampling handlers as (CreateMessageRequest) methods","Run a startup-time annotation audit that validates every @McpSampling method signature","Enable fail-fast application startup so registration errors surface before serving traffic"],"tags":["java","mcp","sampling","validation","reflection"],"backgroundTag":"missing-required-argument","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}