spring-projects/spring-ai · error · IllegalArgumentException

Method must return either CompleteResult, CompleteCompletion

Error message

Method must return either CompleteResult, CompleteCompletion, List<String>, or String: 

What it means

SyncMcpCompleteMethodCallback.validateReturnType throws this IllegalArgumentException at callback registration when a @McpComplete method's declared return type is not CompleteResult, CompleteCompletion, List, or String. This fails fast so invalid handlers are rejected before any request is served.

Source

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

		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
	 */
	@Override
	protected void validateReturnType(Method method) {
		Class<?> returnType = method.getReturnType();

		boolean validReturnType = CompleteResult.class.isAssignableFrom(returnType)
				|| CompleteCompletion.class.isAssignableFrom(returnType) || List.class.isAssignableFrom(returnType)
				|| String.class.isAssignableFrom(returnType);

		if (!validReturnType) {
			throw new IllegalArgumentException(
					"Method must return either CompleteResult, CompleteCompletion, List<String>, " + "or String: "
							+ method.getName() + " in " + method.getDeclaringClass().getName() + " returns "
							+ returnType.getName());
		}
	}

	@Override
	protected McpTransportContext resolveTransportContext(Object exchange) {
		if (exchange instanceof McpSyncServerExchange e) {
			return e.transportContext();
		}
		return null;
	}

	/**
	 * Create a new builder.
	 * @return A new builder instance
	 */

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Change the method to return CompleteResult, CompleteCompletion, List<String>, or String
  2. If the method computes something else, add a separate @McpComplete wrapper method that converts the result
  3. Check the import: use java.util.List and java.lang.String, not project-local classes with the same names

Example fix

// before
@McpComplete(promptName = "code")
public Map<String, Object> complete(String value) { return Map.of(); }
// after
@McpComplete(promptName = "code")
public List<String> complete(String value) { return List.of("java", "go"); }
Defensive patterns

Strategy: validation

Validate before calling

for (Method m : bean.getClass().getDeclaredMethods()) {
    if (m.isAnnotationPresent(McpComplete.class)) {
        Class<?> rt = m.getReturnType();
        if (!(CompleteResult.class.isAssignableFrom(rt) || CompleteCompletion.class.isAssignableFrom(rt)
              || List.class.isAssignableFrom(rt) || String.class.isAssignableFrom(rt)))
            throw new IllegalStateException("bad @McpComplete return type on " + m.getName());
    }
}

Type guard

static boolean hasValidCompleteReturnType(Method m) {
    Class<?> rt = m.getReturnType();
    return CompleteResult.class.isAssignableFrom(rt) || CompleteCompletion.class.isAssignableFrom(rt)
        || List.class.isAssignableFrom(rt) || String.class.isAssignableFrom(rt);
}

Try / catch

try { server.addCompletion(handler); }
catch (IllegalArgumentException e) { log.error("registration rejected: {}", e.getMessage()); throw e; }

Prevention

When it happens

Trigger: Annotating a method with @McpComplete whose return type is void, a Map, a custom DTO, an array type, or any other non-supported class; validation runs in the constructor during annotation scanning.

Common situations: Adding @McpComplete to an existing method with an unrelated return type, typos in imports pulling in the wrong String/List, or migrating a tool method to a completion method without changing its signature.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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