spring-projects/spring-ai · error · IllegalArgumentException

List items must be of type String

Error message

List items must be of type String

What it means

SyncMcpCompleteMethodCallback.convertToCompleteResult throws this IllegalArgumentException when a @McpComplete method returns a List whose elements are not all String instances. The completion protocol only supports string suggestions, so any non-String element (Integer, POJO, null) makes the result unconvertible.

Source

Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/complete/SyncMcpCompleteMethodCallback.java:112

		if (result instanceof CompleteResult) {
			return (CompleteResult) result;
		}

		if (result instanceof CompleteCompletion) {
			return new CompleteResult((CompleteCompletion) result);
		}

		if (result instanceof List) {
			List<?> list = (List<?>) result;
			List<String> values = new ArrayList<>();

			for (Object item : list) {
				if (item instanceof String) {
					values.add((String) item);
				}
				else {
					throw new IllegalArgumentException("List items must be of type String");
				}
			}

			return new CompleteResult(new CompleteCompletion(values, values.size(), false));
		}

		if (result instanceof String) {
			return new CompleteResult(new CompleteCompletion(List.of((String) result), 1, false));
		}

		throw new IllegalArgumentException("Unsupported return type: " + result.getClass().getName());
	}

	/**
	 * Validates that the method return type is compatible with the complete callback.
	 * @param method The method to validate
	 * @throws IllegalArgumentException if the return type is not compatible
	 */

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Ensure the returned List contains only String elements
  2. Convert non-String values to strings explicitly with String.valueOf(item) or item.toString() before returning
  3. Filter out null or non-String entries before returning the list
  4. Change the method signature to List<String> so the compiler enforces element type

Example fix

// before
return Arrays.asList("a.txt", 42);
// after
return Arrays.asList("a.txt", "42");
Defensive patterns

Strategy: validation

Validate before calling

List<?> values = myCompleteMethodResult();
if (values.stream().anyMatch(v -> !(v instanceof String)))
    throw new IllegalStateException("completion list must contain only Strings");

Type guard

static boolean isStringList(Object o) {
    return o instanceof List<?> l && l.stream().allMatch(String.class::isInstance);
}

Try / catch

try { CompleteResult r = callback.apply(ctx, req); }
catch (IllegalArgumentException e) { log.error("bad completion payload: {}", e.getMessage()); return new CompleteResult(new CompleteCompletion(List.of(), 0, false)); }

Prevention

When it happens

Trigger: A @McpComplete handler method returns List<Integer>, List<Object>, a List mixing strings and non-strings, or a List containing null entries; convertToCompleteResult is invoked from apply() while handling a completion request from the MCP client.

Common situations: Developers return domain objects or enums as completion values, or collect heterogeneous values from a database into one list, forgetting that MCP completions must be plain strings.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/99b754120c76e9b1. Report an issue: GitHub.