{"record":{"id":"66b9fa9794aac3f6","repo":"spring-projects/spring-ai","slug":"method-must-return-elicitresult","errorCode":null,"errorMessage":"Method must return ElicitResult: ","messagePattern":"Method must return ElicitResult: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/elicitation/SyncMcpElicitationMethodCallback.java","lineNumber":113,"sourceCode":"\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}\n\n\t/**\n\t * Checks if a parameter type is compatible with the exchange type.\n\t * @param paramType The parameter type to check\n\t * @return true if the parameter type is compatible with the exchange type, false\n\t * otherwise\n\t */\n\t@Override\n\tprotected boolean isExchangeType(Class<?> paramType) {\n\t\t// No exchange type for elicitation methods\n\t\treturn false;\n\t}\n\n\t/**\n\t * Create a new builder.","sourceCodeStart":95,"sourceCodeEnd":131,"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#L95-L131","documentation":"validateReturnType() in SyncMcpElicitationMethodCallback enforces that a method registered as an elicitation callback returns ElicitResult or StructuredElicitResult (or a subtype). Any other return type is rejected with IllegalArgumentException at registration time. The MCP elicitation protocol requires the callback to produce an ElicitResult to send back to the client.","triggerScenarios":"Annotating a method with @McpElicitation whose return type is void, String, Map, or any non-ElicitResult type; building a SyncMcpElicitationMethodCallback via its builder with such a method.","commonSituations":"Developer treats the annotation like a plain event handler and returns void or a domain object; refactoring changed the return type after registration code was written; copy-pasted handler from a logging/notification callback.","solutions":["Change the method return type to ElicitResult (or StructuredElicitResult)","Wrap the current return value in an ElicitResult with the appropriate action","If the method is not meant to be an elicitation handler, remove the @McpElicitation annotation"],"exampleFix":"// before\n@McpElicitation(clients = \"client1\")\npublic void handleForm(ElicitRequest request) { ... }\n// after\n@McpElicitation(clients = \"client1\")\npublic ElicitResult handleForm(ElicitRequest request) {\n    return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of());\n}","handlingStrategy":"validation","validationCode":"Method m = handler.getClass().getDeclaredMethod(\"askUser\", ElicitRequest.class);\nif (!ElicitResult.class.isAssignableFrom(m.getReturnType())\n        && !StructuredElicitResult.class.isAssignableFrom(m.getReturnType())) {\n    throw new IllegalStateException(\"return type must be ElicitResult/StructuredElicitResult\");\n}","typeGuard":"static boolean isValidElicitationHandler(Method m) {\n    return ElicitResult.class.isAssignableFrom(m.getReturnType())\n        || StructuredElicitResult.class.isAssignableFrom(m.getReturnType());\n}","tryCatchPattern":null,"preventionTips":["Declare the return type as ElicitResult at the method signature level","Never return void or domain objects from @McpElicitation methods","Write a unit test asserting every annotated method passes validateReturnType"],"tags":["mcp","elicitation","return-type","java"],"backgroundTag":"type-mismatch","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"}