{"record":{"id":"0f445674429cf9c7","repo":"spring-projects/spring-ai","slug":"method-must-have-either-readresourcerequest-or-str","errorCode":null,"errorMessage":"Method must have either ReadResourceRequest or String parameter when no URI variables are present: ${method} in ${declaringClass}","messagePattern":"Method must have either ReadResourceRequest or String parameter when no URI variables are present: (.+?) in (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/AbstractMcpResourceMethodCallback.java","lineNumber":267,"sourceCode":"\t\t\t\t\t|| String.class.isAssignableFrom(paramType)) {\n\t\t\t\tif (hasRequestOrUriParam) {\n\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\"Method cannot have more than one ReadResourceRequest or String parameter: \"\n\t\t\t\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName());\n\t\t\t\t}\n\t\t\t\thasRequestOrUriParam = true;\n\t\t\t\thasValidParams = true;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\"Method parameters must be exchange, ReadResourceRequest, String, McpMeta, or @McpProgressToken when no URI variables are present: \"\n\t\t\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName()\n\t\t\t\t\t\t\t\t+ \" has parameter of type \" + paramType.getName());\n\t\t\t}\n\t\t}\n\n\t\tif (!hasValidParams && nonSpecialParamCount > 0) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Method must have either ReadResourceRequest or String parameter when no URI variables are present: \"\n\t\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName());\n\t\t}\n\t}\n\n\tprotected void validateParamType(Class<?> paramType) {\n\t}\n\n\t/**\n\t * Validates method parameters when URI variables are present. This method provides\n\t * common validation logic and 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 validateParametersWithUriVariables(Method method) {\n\t\tParameter[] parameters = method.getParameters();\n\n\t\t// Count special parameters (exchange, request, progress token, and meta)","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/AbstractMcpResourceMethodCallback.java#L249-L285","documentation":"Thrown when a URI-variable-less resource method has non-special parameters (excluding @McpProgressToken and McpMeta) but none of them is a ReadResourceRequest or String. The framework requires the resource payload (the read request or URI string) to be bindable, so such a signature is rejected.","triggerScenarios":"A @McpResource method with no URI variables whose parameters are only context/exchange/McpMeta types (hasValidParams stays false while nonSpecialParamCount > 0).","commonSituations":"Writing a resource handler that takes only a context, forgetting the request/URI parameter; deleting the String parameter during a refactor while keeping others.","solutions":["Add a ReadResourceRequest parameter to receive the resource read request","Or add a String parameter to receive the URI","If the method needs no request data at all, ensure non-special parameters are removed so validation passes"],"exampleFix":"// before\n@McpResource(uri = \"cfg://app\")\npublic String read(McpSyncRequestContext ctx) { ... }\n// after\n@McpResource(uri = \"cfg://app\")\npublic String read(ReadResourceRequest request, McpSyncRequestContext ctx) { ... }","handlingStrategy":"validation","validationCode":"boolean hasPayload = Arrays.stream(method.getParameterTypes())\n    .anyMatch(t -> ReadResourceRequest.class.isAssignableFrom(t) || String.class.isAssignableFrom(t));\nif (!hasPayload) throw new IllegalStateException(method + \" lacks a ReadResourceRequest/String parameter\");","typeGuard":null,"tryCatchPattern":"try { provider.build(...); }\ncatch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"must have either ReadResourceRequest or String parameter\")) { /* add the missing request param */ }\n  else throw e;\n}","preventionTips":["Always include a ReadResourceRequest or String parameter in resource handlers","Check signatures after refactoring to ensure the payload parameter was not dropped"],"tags":["mcp","spring-ai","missing-parameter","method-validation"],"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-23T08:17:48.524Z"}