{"record":{"id":"9011043dad7f084f","repo":"spring-projects/spring-ai","slug":"error-invoking-prompt-list-changed-consumer-method","errorCode":null,"errorMessage":"Error invoking prompt list changed consumer method: ","messagePattern":"Error invoking prompt list changed consumer method: ","errorType":"exception","errorClass":"McpPromptListChangedConsumerMethodException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/changed/prompt/SyncMcpPromptListChangedMethodCallback.java","lineNumber":68,"sourceCode":"\t * the prompt list changed consumer method\n\t * @throws IllegalArgumentException if the updatedPrompts is null\n\t */\n\t@Override\n\tpublic void accept(List<McpSchema.Prompt> updatedPrompts) {\n\t\tif (updatedPrompts == null) {\n\t\t\tthrow new IllegalArgumentException(\"Updated prompts list must not be null\");\n\t\t}\n\n\t\ttry {\n\t\t\t// Build arguments for the method call\n\t\t\tObject[] args = this.buildArgs(this.method, null, updatedPrompts);\n\n\t\t\t// Invoke the method\n\t\t\tthis.method.setAccessible(true);\n\t\t\tthis.method.invoke(this.bean, args);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new McpPromptListChangedConsumerMethodException(\n\t\t\t\t\t\"Error invoking prompt list changed consumer method: \" + this.method.getName(), e);\n\t\t}\n\t}\n\n\t/**\n\t * Validates that the method return type is compatible with the prompt list changed\n\t * consumer callback.\n\t * @param method The method to validate\n\t * @throws IllegalArgumentException if the return type is not compatible\n\t */\n\t@Override\n\tprotected void validateReturnType(Method method) {\n\t\tClass<?> returnType = method.getReturnType();\n\n\t\tif (returnType != void.class) {\n\t\t\tthrow new IllegalArgumentException(\"Method must have void return type: \" + method.getName() + \" in \"\n\t\t\t\t\t+ method.getDeclaringClass().getName() + \" returns \" + returnType.getName());\n\t\t}","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/changed/prompt/SyncMcpPromptListChangedMethodCallback.java#L50-L86","documentation":"A McpPromptListChangedConsumerMethodException wrapping the underlying failure, thrown by SyncMcpPromptListChangedMethodCallback.accept() when the reflective this.method.invoke(bean, args) call fails. Any exception raised inside the user's handler method (or by argument building/IllegalAccess) is wrapped, with the handler method name in the message and the original cause attached.","triggerScenarios":"The handler method throws at runtime (NPE, repository failure, etc.); the method's parameter types do not match the built arguments (IllegalArgumentException from reflection); the method is not accessible and setAccessible fails under a SecurityManager/JPMS strong encapsulation.","commonSituations":"Handler signature drift — declaring the consumer with wrong parameter types after an MCP SDK upgrade; handler code throwing due to downstream service failures; Java 16+ module encapsulation blocking setAccessible on non-exported classes.","solutions":["Inspect getCause() of the McpPromptListChangedConsumerMethodException — the real fix belongs to the wrapped exception.","Match the handler signature exactly to the expected consumer shape, e.g. void handler(List<McpSchema.Prompt> prompts).","Fix the bug in the handler body (null checks, downstream calls) that caused the wrapped exception.","For JPMS access errors, open the package (–add-opens) or keep handler methods in an exported/open package."],"exampleFix":"// before (signature mismatch -> reflective invoke fails)\npublic void onPromptsChanged(PromptListChangedNotification n) { ... }\n// after\npublic void onPromptsChanged(List<McpSchema.Prompt> updatedPrompts) { ... }","handlingStrategy":"try-catch","validationCode":"Method m = bean.getClass().getMethod(\"onPromptsChanged\", List.class);\n// parameter type check before wiring\nif (!List.class.isAssignableFrom(m.getParameterTypes()[0])) {\n    throw new IllegalStateException(\"handler must accept List<McpSchema.Prompt>\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    callback.accept(updatedPrompts);\n} catch (McpPromptListChangedConsumerMethodException e) {\n    Throwable cause = e.getCause();\n    log.error(\"Prompt list changed handler {} failed: {}\", e.getMessage(), cause, cause);\n    // handle or rethrow based on cause type\n}","preventionTips":["Always inspect getCause() — the wrapped exception carries the real problem.","Match handler parameter types exactly to List<McpSchema.Prompt>.","Keep handlers in open/exported packages for reflective access under JPMS.","Wrap risky handler logic in its own try-catch to avoid propagating failures."],"tags":["mcp","reflective-invocation","wrapped-exception","callback"],"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"}