{"record":{"id":"da82f2fba8e51434","repo":"spring-projects/spring-ai","slug":"list-items-must-be-of-type-string-da82f2","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/SyncStatelessMcpCompleteMethodCallback.java","lineNumber":111,"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":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/complete/SyncStatelessMcpCompleteMethodCallback.java#L93-L129","documentation":"SyncStatelessMcpCompleteMethodCallback.convertToCompleteResult throws this IllegalArgumentException when a @McpComplete method returns a List containing non-String elements. MCP completion values must be strings, so the stateless callback cannot build a CompleteCompletion from mixed or typed lists.","triggerScenarios":"A stateless @McpComplete handler returns List<Integer>, List<Object>, List<Enum>, or a List with null/mixed elements; triggered from apply() while serving a completion request.","commonSituations":"Returning enum constants or numeric IDs as suggestions, or streaming rows from a repository into an untyped list.","solutions":["Return List<String> with only String values","Map elements to strings: list.stream().map(String::valueOf).collect(toList())","Remove or skip null and non-String entries before returning","Declare the method return type as List<String> for compile-time safety"],"exampleFix":"// before\nreturn ids; // List<Integer>\n// after\nreturn ids.stream().map(String::valueOf).toList();","handlingStrategy":"validation","validationCode":"List<?> values = handlerResult();\nif (values.stream().anyMatch(v -> !(v instanceof String)))\n    throw new IllegalStateException(\"stateless completion values must be Strings\");","typeGuard":"static boolean isAllStrings(List<?> l) { return l.stream().allMatch(String.class::isInstance); }","tryCatchPattern":"try { return callback.apply(context, request); }\ncatch (IllegalArgumentException e) { log.warn(\"non-string completion item: {}\", e.getMessage()); return new CompleteResult(new CompleteCompletion(List.of(), 0, false)); }","preventionTips":["Use List<String> as the declared return type","Convert enums/IDs with String.valueOf before returning","Filter nulls: list.stream().filter(Objects::nonNull).map(String::valueOf).toList()"],"tags":["java","mcp","completion","type-mismatch"],"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"}