{"record":{"id":"421560768cc43f5b","repo":"spring-projects/spring-ai","slug":"list-items-must-be-of-type-string-421560","errorCode":null,"errorMessage":"List items must be of type String","messagePattern":"List items must be of type String","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/complete/AsyncStatelessMcpCompleteMethodCallback.java","lineNumber":124,"sourceCode":"\n\t\tif (result instanceof CompleteResult) {\n\t\t\treturn (CompleteResult) result;\n\t\t}\n\n\t\tif (result instanceof CompleteCompletion) {\n\t\t\treturn new CompleteResult((CompleteCompletion) result);\n\t\t}\n\n\t\tif (result instanceof List) {\n\t\t\tList<?> list = (List<?>) result;\n\t\t\tList<String> values = new ArrayList<>();\n\n\t\t\tfor (Object item : list) {\n\t\t\t\tif (item instanceof String) {\n\t\t\t\t\tvalues.add((String) item);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new IllegalArgumentException(\"List items must be of type String\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn new CompleteResult(new CompleteCompletion(values, values.size(), false));\n\t\t}\n\n\t\tif (result instanceof String) {\n\t\t\treturn new CompleteResult(new CompleteCompletion(List.of((String) result), 1, false));\n\t\t}\n\n\t\tthrow new IllegalArgumentException(\"Unsupported return type: \" + result.getClass().getName());\n\t}\n\n\t/**\n\t * Validates that the method return type is compatible with the complete callback.\n\t * @param method The method to validate\n\t * @throws IllegalArgumentException if the return type is not compatible\n\t */","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/complete/AsyncStatelessMcpCompleteMethodCallback.java#L106-L142","documentation":"This library converts the annotated @McpComplete method's return value into an MCP CompleteResult. When the return value is a List, every element must be a String, because MCP completion values are strings. If any element is not a String, the AsyncStatelessMcpCompleteMethodCallback throws this IllegalArgumentException instead of emitting invalid completion data.","triggerScenarios":"An @McpComplete method returns (or wraps in Mono) a List containing non-String elements (e.g. List<Integer>, List<MyEnum>, List<Object> built from user data), which convertToCompleteResult iterates via the instanceof String check.","commonSituations":"Returning enums, numeric IDs, or domain objects directly instead of mapping them to strings; untyped raw Lists from legacy code; building suggestions from a database column that returns non-string types.","solutions":["Map the returned list elements to strings before returning: list.stream().map(String::valueOf).collect(Collectors.toList())","Ensure the annotated method is declared to return List<String> or Mono<List<String>> so type errors surface at compile time","Call toString()/name()/mapping on enum or object elements before returning","Catch IllegalArgumentException in a wrapper if dynamic data cannot be guaranteed"],"exampleFix":"// before\nreturn List.of(42, 99); // List<Integer>\n// after\nreturn List.of(\"42\", \"99\"); // List<String>","handlingStrategy":"validation","validationCode":"Object result = myMethod();\nif (result instanceof List<?> list && list.stream().allMatch(String.class::isInstance)) {\n    return callback.apply(exchange, request); // safe\n}","typeGuard":"static boolean isStringList(Object o) {\n    return o instanceof List<?> l && l.stream().allMatch(String.class::isInstance);\n}","tryCatchPattern":"try {\n    return callback.apply(exchange, request);\n} catch (IllegalArgumentException e) {\n    log.error(\"Completion result contained non-string items\", e);\n    return new CompleteResult(new CompleteCompletion(List.of(), 0, false));\n}","preventionTips":["Declare completion methods as List<String> or Mono<List<String>> so the compiler enforces element types","Map domain objects/enums to strings at the method boundary","Add unit tests that call the annotated method and assert every element is a String","Avoid raw List returns from legacy code paths"],"tags":["mcp","completion","type-mismatch","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-14T05:17:10.506Z"}