{"record":{"id":"276f6579cc2ce210","repo":"spring-projects/spring-ai","slug":"multiple-sampling-methods-found-count","errorCode":null,"errorMessage":"Multiple sampling methods found: <count>","messagePattern":"Multiple sampling methods found: <count>","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/provider/sampling/AsyncMcpSamplingProvider.java","lineNumber":116,"sourceCode":"\t\t\t\t\tFunction<CreateMessageRequest, Mono<CreateMessageResult>> methodCallback = AsyncMcpSamplingMethodCallback\n\t\t\t\t\t\t.builder()\n\t\t\t\t\t\t.method(mcpSamplingMethod)\n\t\t\t\t\t\t.bean(samplingObject)\n\t\t\t\t\t\t.sampling(samplingAnnotation)\n\t\t\t\t\t\t.build();\n\n\t\t\t\t\treturn new AsyncSamplingSpecification(samplingAnnotation.clients(), methodCallback);\n\t\t\t\t})\n\t\t\t\t.toList())\n\t\t\t.flatMap(List::stream)\n\t\t\t.toList();\n\n\t\tif (samplingHandlers.isEmpty()) {\n\t\t\tlogger.warn(\"No sampling methods found\");\n\t\t}\n\t\tif (samplingHandlers.size() > 1) {\n\t\t\tif (logger.isWarnEnabled()) {\n\t\t\t\tlogger.warn(\"Multiple sampling methods found: \" + samplingHandlers.size());\n\t\t\t}\n\t\t}\n\n\t\treturn samplingHandlers;\n\t}\n\n\t/**\n\t * Returns the methods of the given bean class.\n\t * @param bean the bean instance\n\t * @return the methods of the bean class\n\t */\n\tprotected Method[] doGetClassMethods(Object bean) {\n\t\treturn bean.getClass().getDeclaredMethods();\n\t}\n\n}\n","sourceCodeStart":98,"sourceCodeEnd":133,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/provider/sampling/AsyncMcpSamplingProvider.java#L98-L133","documentation":"AsyncMcpSamplingProvider.getSamplingSpecifictions logs 'Multiple sampling methods found: N' when more than one @McpSampling-annotated method is discovered across the configured objects. Some MCP server wiring expects a single sampling handler, so multiple handlers may be ambiguous or only the first may take effect depending on downstream usage.","triggerScenarios":"Two or more public methods annotated with @McpSampling exist across the sampling objects passed to AsyncMcpSamplingProvider; samplingHandlers.size() > 1 triggers the warn at AsyncMcpSamplingProvider.java:116.","commonSituations":"Accidental duplicate handler methods added during refactoring; multiple sampling classes registered where one combined handler was intended; copy-paste leaving an old sampling method in place alongside the new one.","solutions":["Keep exactly one @McpSampling-annotated method across the registered sampling objects, consolidating logic into it.","Remove stale/duplicate annotated methods or drop their @McpSampling annotation.","If multiple handlers are intentional, confirm downstream registration supports all of them; otherwise split into separate providers."],"exampleFix":"// before\nclass Sampler { @McpSampling CompletableFuture<String> a(...) {...} @McpSampling CompletableFuture<String> b(...) {...} }\n// after\nclass Sampler { @McpSampling CompletableFuture<String> handle(...) { /* merged logic */ } }","handlingStrategy":"validation","validationCode":"// Enforce a single sampling handler before registration:\nlong count = Arrays.stream(samplingObjects)\n    .flatMapToLong(o -> Arrays.stream(o.getClass().getDeclaredMethods())\n        .filter(m -> m.isAnnotationPresent(McpSampling.class)))\n    .count();\nif (count > 1) throw new IllegalStateException(\"Only one @McpSampling method allowed, found \" + count);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep exactly one @McpSampling method per provider registration.","Remove leftover experimental sampling methods promptly.","Add an architecture test banning duplicate @McpSampling annotations across handler classes.","Route multiple behaviors through one handler method with internal dispatch."],"tags":["mcp","annotation-scanning","sampling","duplicate-handlers"],"backgroundTag":"conflicting-config-options","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"}