{"record":{"id":"b92482bde96463ee","repo":"spring-projects/spring-ai","slug":"single-parameter-must-be-of-type-createmessagerequ","errorCode":null,"errorMessage":"Single parameter must be of type CreateMessageRequest: {methodName} in {className} has parameter of type {paramTypeName}","messagePattern":"Single parameter must be of type CreateMessageRequest: (.+?) in (.+?) has parameter of type (.+?)","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":102,"sourceCode":"\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(\n\t\t\t\t\t\"Currently only methods with a single CreateMessageRequest parameter are supported: \"\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\t}\n\n\t/**\n\t * Builds the arguments array for invoking the method.\n\t * <p>","sourceCodeStart":84,"sourceCodeEnd":120,"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#L84-L120","documentation":"With exactly one parameter, that parameter must be assignable from CreateMessageRequest, since it receives the sampling request. validateParameters() throws IllegalArgumentException when the single parameter has an incompatible type.","triggerScenarios":"Declaring a sampling callback like `void handler(String req)` or `void handler(MyCustomRequest req)` — a single parameter whose type is not CreateMessageRequest (or a subtype).","commonSituations":"Developers use their own request DTO, a String, or Map<String,Object> instead of the framework's CreateMessageRequest; or they upgraded spring-ai-mcp where the required request type changed.","solutions":["Change the single parameter type to CreateMessageRequest (or a subclass)","If you need custom input parsing, accept CreateMessageRequest and map its fields manually inside the method","Check the MCP SDK version's schema class (io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest) and import the correct one"],"exampleFix":"// before\n@McpSampling\npublic String sample(String request) { return \"reply\"; }\n// after\n@McpSampling\npublic String sample(CreateMessageRequest request) { return \"reply\"; }","handlingStrategy":"type-guard","validationCode":"for (Method m : clazz.getDeclaredMethods()) {\n    if (m.isAnnotationPresent(McpSampling.class)\n            && m.getParameterCount() == 1\n            && !CreateMessageRequest.class.isAssignableFrom(m.getParameterTypes()[0])) {\n        throw new IllegalStateException(m.getName()\n            + \" parameter must be CreateMessageRequest, got \"\n            + m.getParameterTypes()[0].getName());\n    }\n}","typeGuard":"boolean hasCreateMessageRequestParam(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().startsWith(\"Single parameter must be of type\")) {\n        log.error(\"Wrong parameter type on sampling method {}: {}\", method, e.getMessage());\n    } else { throw e; }\n}","preventionTips":["Import CreateMessageRequest from the MCP schema package, not a custom DTO","Pin the spring-ai-mcp version and re-check signatures after upgrades","Add an ArchUnit or unit test asserting handler parameter types"],"tags":["java","mcp","sampling","type-mismatch"],"backgroundTag":"type-mismatch","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"}