{"record":{"id":"c328a68959893ba1","repo":"spring-projects/spring-ai","slug":"method-must-return-elicitresult-or-structuredelici","errorCode":null,"errorMessage":"Method must return ElicitResult or StructuredElicitResult: ","messagePattern":"Method must return ElicitResult or StructuredElicitResult: ","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/elicitation/SyncMcpElicitationMethodCallback.java","lineNumber":92,"sourceCode":"\t\t\t\tvar content = structuredElicitResult.structuredContent() != null\n\t\t\t\t\t\t? jsonHelper.convertToMap(structuredElicitResult.structuredContent()) : null;\n\n\t\t\t\treturn ElicitResult.builder(structuredElicitResult.action())\n\t\t\t\t\t.content(content)\n\t\t\t\t\t.meta(structuredElicitResult.meta())\n\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","sourceCodeStart":74,"sourceCodeEnd":110,"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#L74-L110","documentation":"Thrown as IllegalStateException by SyncMcpElicitationMethodCallback.apply when the sync handler method's declared return type is neither ElicitResult nor StructuredElicitResult. Unlike the async variant (which validates return type upfront), the sync callback checks the actual returned object at invocation time; simple types and custom objects are a TODO.","triggerScenarios":"A sync @McpElicitation handler returning String, Map, custom POJO, void, or any other type: public String confirm(ElicitRequest req). The method must return ElicitResult or StructuredElicitResult.","commonSituations":"Developers expect the framework to wrap returned simple values into an ElicitResult automatically (that support is not implemented), or they return the elicited value directly instead of building an ElicitResult.","solutions":["Change the handler to return ElicitResult, e.g. ElicitResult.builder()...build() with the accepted action and content map","Return StructuredElicitResult for typed/structured responses","Wrap simple values manually: new ElicitResult(ElicitResult.Action.ACCEPT, Map.of(\"value\", myValue))","Note validateReturnType typically catches this earlier; if you see it at runtime, the method was registered without return-type validation"],"exampleFix":"// before\n@McpElicitation\npublic String confirm(ElicitRequest request) { return \"yes\"; }\n\n// after\n@McpElicitation\npublic ElicitResult confirm(ElicitRequest request) {\n    return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of(\"answer\", \"yes\"));\n}\n","handlingStrategy":"validation","validationCode":"Class<?> rt = handlerMethod.getReturnType();\nif (!ElicitResult.class.isAssignableFrom(rt) && !StructuredElicitResult.class.isAssignableFrom(rt)) {\n    throw new IllegalArgumentException(\"Sync handler must return ElicitResult or StructuredElicitResult: \" + handlerMethod);\n}","typeGuard":"boolean isValidSyncReturnType(Method m) {\n    return ElicitResult.class.isAssignableFrom(m.getReturnType())\n        || StructuredElicitResult.class.isAssignableFrom(m.getReturnType());\n}","tryCatchPattern":"try {\n    return callback.apply(request);\n} catch (IllegalStateException e) {\n    log.error(\"Handler returns unsupported type: {}\", e.getMessage());\n    throw e;\n}","preventionTips":["Always return ElicitResult or StructuredElicitResult from sync handlers","Build ElicitResult explicitly with an Action (ACCEPT/DECLINE/CANCEL) and content map","Rely on validateReturnType at registration time so this surfaces before runtime"],"tags":["java","mcp","elicitation","return-type","unsupported-operation"],"backgroundTag":"unsupported-operation","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"}