{"record":{"id":"9b258912a4f3e30f","repo":"spring-projects/spring-ai","slug":"method-cannot-have-more-than-one-request-context-p-9b2589","errorCode":null,"errorMessage":"Method cannot have more than one request context parameter: ${method} in ${declaringClass}","messagePattern":"Method cannot have more than one request context parameter: (.+?) 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":212,"sourceCode":"\n\t\t// Check parameter types\n\t\tboolean hasValidParams = false;\n\t\tboolean hasExchangeParam = false;\n\t\tboolean hasRequestOrUriParam = false;\n\t\tboolean hasMetaParam = false;\n\t\tboolean hasRequestContextParam = false;\n\n\t\tfor (Parameter param : parameters) {\n\t\t\t// Skip @McpProgressToken annotated parameters\n\t\t\tif (param.isAnnotationPresent(McpProgressToken.class)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tClass<?> paramType = param.getType();\n\n\t\t\tif (McpSyncRequestContext.class.isAssignableFrom(paramType)) {\n\t\t\t\tif (hasRequestContextParam) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Method cannot have more than one request context parameter: \"\n\t\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName());\n\t\t\t\t}\n\t\t\t\tif (McpPredicates.isReactiveReturnType.test(method)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\"Sync complete methods should use McpSyncRequestContext instead of McpAsyncRequestContext 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\thasRequestContextParam = true;\n\t\t\t}\n\t\t\telse if (McpAsyncRequestContext.class.isAssignableFrom(paramType)) {\n\t\t\t\tif (hasRequestContextParam) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Method cannot have more than one request context parameter: \"\n\t\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName());\n\t\t\t\t}\n\t\t\t\tif (McpPredicates.isNotReactiveReturnType.test(method)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\"Async complete methods should use McpAsyncRequestContext instead of McpSyncRequestContext parameter: \"\n\t\t\t\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName());","sourceCodeStart":194,"sourceCodeEnd":230,"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#L194-L230","documentation":"A @McpResource method declared more than one McpSyncRequestContext (or compatible request-context) parameter. Only one request context is provided per invocation, so validateParametersWithoutUriVariables throws IllegalArgumentException on the second occurrence.","triggerScenarios":"Declaring two parameters of type McpSyncRequestContext (or subtypes) in a resource method; the validator tracks a hasRequestContextParam flag and throws when a second context-typed parameter is seen.","commonSituations":"Copy-paste duplication of a context parameter; refactoring that added a context parameter without removing the old one; confusion between progress-token/meta special params and the request context.","solutions":["Remove the duplicate McpSyncRequestContext parameter, keeping exactly one.","Move shared data into method-local variables or a service rather than extra context parameters.","If a second context-like value is needed, derive it from the single context inside the method body."],"exampleFix":"// before\n@McpResource(uri = \"file:///docs/{name}\")\npublic String read(McpSyncRequestContext ctx, McpSyncRequestContext ctx2, String name) { ... }\n\n// after\n@McpResource(uri = \"file:///docs/{name}\")\npublic String read(McpSyncRequestContext ctx, String name) { ... }","handlingStrategy":"validation","validationCode":"static long countContextParams(Method m) {\n    return java.util.Arrays.stream(m.getParameterTypes())\n        .filter(t -> McpSyncRequestContext.class.isAssignableFrom(t)).count();\n}\n// assert countContextParams(m) <= 1;","typeGuard":null,"tryCatchPattern":"try {\n    resourceManager.register(callback);\n} catch (IllegalArgumentException e) {\n    throw new IllegalStateException(\"Duplicate McpSyncRequestContext parameter\", e);\n}","preventionTips":["Declare exactly one request-context parameter per resource method.","Check for duplicated parameters after refactors.","Derive extra context data from the single context object in the body."],"tags":["mcp","java","duplicate-parameter","request-context","resource-method"],"backgroundTag":"invalid-argument-value","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"}