{"record":{"id":"a0e883a8bc0640f7","repo":"spring-projects/spring-ai","slug":"error-invoking-elicitation-method","errorCode":null,"errorMessage":"Error invoking elicitation method: ","messagePattern":"Error invoking elicitation method: ","errorType":"exception","errorClass":"McpElicitationMethodException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/elicitation/SyncMcpElicitationMethodCallback.java","lineNumber":98,"sourceCode":"\t\t\t\t\t.build();\n\t\t\t}\n\t\t\telse if (this.method.getReturnType().isAssignableFrom(ElicitResult.class)) {\n\t\t\t\t// If the method returns ElicitResult, return it directly\n\t\t\t\treturn (ElicitResult) result;\n\n\t\t\t}\n\t\t\telse {\n\n\t\t\t\t// TODO add support for methods returning simple types or Objects of\n\t\t\t\t// elicitation schema type.\n\n\t\t\t\tthrow new IllegalStateException(\"Method must return ElicitResult or StructuredElicitResult: \"\n\t\t\t\t\t\t+ this.method.getName() + \" in \" + this.method.getDeclaringClass().getName() + \" returns \"\n\t\t\t\t\t\t+ this.method.getReturnType().getName());\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new McpElicitationMethodException(\"Error invoking elicitation 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 elicitation 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 (!ElicitResult.class.isAssignableFrom(returnType)\n\t\t\t\t&& !StructuredElicitResult.class.isAssignableFrom(returnType)) {\n\t\t\tthrow new IllegalArgumentException(\"Method must return ElicitResult: \" + method.getName() + \" in \"\n\t\t\t\t\t+ method.getDeclaringClass().getName() + \" returns \" + returnType.getName());\n\t\t}\n\t}","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/elicitation/SyncMcpElicitationMethodCallback.java#L80-L116","documentation":"SyncMcpElicitationMethodCallback.apply() wraps any exception thrown while reflectively invoking a @McpElicitation-annotated method into an McpElicitationMethodException. The original cause (e.g. a user-code NPE or return-type mismatch) is preserved as the cause. It signals that the elicitation callback method itself failed during execution, not that the MCP protocol exchange failed.","triggerScenarios":"Registering a @McpElicitation handler method whose body throws at runtime; the method returns a type other than ElicitResult/StructuredElicitResult; reflection on the method fails (IllegalAccessException).","commonSituations":"User handler code dereferences a null request argument; an annotated method with the wrong return type slipped past compile-time checks (e.g. raw types); tests like testNullRequest pass a null request and the handler throws.","solutions":["Inspect the cause chain (getCause()) of the McpElicitationMethodException to find the real failure in your handler method","Ensure the annotated method's return type is ElicitResult or StructuredElicitResult","Add null/argument checks inside your elicitation handler before using the request","Fix visibility of the annotated method (must be accessible for reflection)"],"exampleFix":"// before\n@McpElicitation(clients = \"client1\")\npublic String askUser(ElicitRequest request) { return request.message(); }\n// after\n@McpElicitation(clients = \"client1\")\npublic ElicitResult askUser(ElicitRequest request) {\n    if (request == null) { throw new IllegalArgumentException(\"request required\"); }\n    return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of());\n}","handlingStrategy":"try-catch","validationCode":"for (Method m : handler.getClass().getDeclaredMethods()) {\n    if (m.isAnnotationPresent(McpElicitation.class)\n            && !ElicitResult.class.isAssignableFrom(m.getReturnType())\n            && !StructuredElicitResult.class.isAssignableFrom(m.getReturnType())) {\n        throw new IllegalStateException(m.getName() + \" must return ElicitResult or StructuredElicitResult\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    elicitationCallback.apply(exchange, request);\n} catch (McpElicitationMethodException e) {\n    logger.error(\"elicitation handler failed\", e.getCause());\n    return new ElicitResult(ElicitResult.Action.DENY, Map.of());\n}","preventionTips":["Keep handler bodies minimal and null-check the request argument first","Always return ElicitResult/StructuredElicitResult from @McpElicitation methods","Add a startup self-test that invokes each handler with a sample request"],"tags":["mcp","reflection","elicitation","java"],"backgroundTag":"unexpected-api-response-shape","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"}