spring-projects/spring-ai · error · IllegalArgumentException

Method must have void return type:

Error message

Method must have void return type: 

What it means

IllegalArgumentException thrown by SyncMcpPromptListChangedMethodCallback.validateReturnType() when a registered handler method does not return void. The sync callback invokes the handler synchronously and discards any result, so only void methods are accepted; the message includes the method name, declaring class, and offending return type.

Source

Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/changed/prompt/SyncMcpPromptListChangedMethodCallback.java:84

		}
		catch (Exception e) {
			throw new McpPromptListChangedConsumerMethodException(
					"Error invoking prompt list changed consumer method: " + this.method.getName(), e);
		}
	}

	/**
	 * Validates that the method return type is compatible with the prompt list changed
	 * consumer 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();

		if (returnType != void.class) {
			throw new IllegalArgumentException("Method must have void return type: " + method.getName() + " in "
					+ method.getDeclaringClass().getName() + " returns " + returnType.getName());
		}
	}

	/**
	 * Create a new builder.
	 * @return A new builder instance
	 */
	public static Builder builder() {
		return new Builder();
	}

	/**
	 * Builder for creating SyncMcpPromptListChangedMethodCallback instances.
	 * <p>
	 * This builder provides a fluent API for constructing
	 * SyncMcpPromptListChangedMethodCallback instances with the required parameters.
	 */

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Change the handler to return void and perform side effects inside the body.
  2. If you need reactive/async handling, use AsyncMcpPromptListChangedMethodCallback with a Mono<Void> return type instead.
  3. If the method must return data, it belongs to a different callback type (tool/resource), not the list-changed consumer.

Example fix

// before
public boolean onPromptsChanged(List<McpSchema.Prompt> prompts) {
    return reload(prompts);
}
// after
public void onPromptsChanged(List<McpSchema.Prompt> prompts) {
    reload(prompts);
}
Defensive patterns

Strategy: validation

Validate before calling

if (method.getReturnType() != void.class) {
    throw new IllegalStateException("sync @McpPromptListChanged handlers must return void: " + method);
}

Type guard

static boolean validSyncReturnType(Method m) {
    return m.getReturnType() == void.class;
}

Try / catch

try {
    registerSyncHandler(method, bean);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Method must have void return type")) {
        throw new ConfigurationException("Handler must be void: " + e.getMessage(), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Building/registering a SyncMcpPromptListChangedMethodCallback whose method returns boolean, String, List, Mono, CompletableFuture, etc. Validation happens at callback construction, so the error appears at wiring/startup time.

Common situations: Reusing an existing query method that returns data as a changed-handler; registering an async-style (Mono-returning) handler against the sync callback; switching from Async to Sync callback without removing return values.

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