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

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.

Source

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

		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. Return List<String> with only String values
  2. Map elements to strings: list.stream().map(String::valueOf).collect(toList())
  3. Remove or skip null and non-String entries before returning
  4. Declare the method return type as List<String> for compile-time safety

Example fix

// before
return ids; // List<Integer>
// after
return ids.stream().map(String::valueOf).toList();
Defensive patterns

Strategy: validation

Validate before calling

List<?> values = handlerResult();
if (values.stream().anyMatch(v -> !(v instanceof String)))
    throw new IllegalStateException("stateless completion values must be Strings");

Type guard

static boolean isAllStrings(List<?> l) { return l.stream().allMatch(String.class::isInstance); }

Try / catch

try { return callback.apply(context, request); }
catch (IllegalArgumentException e) { log.warn("non-string completion item: {}", e.getMessage()); return new CompleteResult(new CompleteCompletion(List.of(), 0, false)); }

Prevention

When it happens

Trigger: 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.

Common situations: Returning enum constants or numeric IDs as suggestions, or streaming rows from a repository into an untyped list.

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/da82f2fba8e51434. Report an issue: GitHub.