spring-projects/spring-ai · error · java.lang.IllegalArgumentException
Synchronous progress methods must return void: {method.getNa
Error message
Synchronous progress methods must return void: {method.getName()} in {method.getDeclaringClass().getName()} returns {method.getReturnType().getName()} What it means
Synchronous (@McpProgress with SyncMcpProgressMethodCallback) progress methods must return void. validateReturnType throws this IllegalArgumentException when the method declares any other return type, because the sync callback processes the notification inline and discards results.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/progress/SyncMcpProgressMethodCallback.java:43
* Synchronous implementation of a progress method callback.
*
* This class creates a Consumer that invokes a method annotated with @McpProgress
* synchronously when a progress notification is received.
*
* @author Christian Tzolov
*/
public final class SyncMcpProgressMethodCallback extends AbstractMcpProgressMethodCallback
implements Consumer<ProgressNotification> {
private SyncMcpProgressMethodCallback(Builder builder) {
super(builder.method, builder.bean);
}
@Override
protected void validateReturnType(Method method) {
// Synchronous methods must return void
if (!void.class.equals(method.getReturnType())) {
throw new IllegalArgumentException("Synchronous progress methods must return void: " + method.getName()
+ " in " + method.getDeclaringClass().getName() + " returns " + method.getReturnType().getName());
}
}
/**
* Accept the progress notification and process it.
* <p>
* This method builds the arguments for the method call and invokes the method.
* @param notification The progress notification, must not be null
* @throws McpProgressMethodException if there is an error invoking the progress
* method
* @throws IllegalArgumentException if the notification is null
*/
@Override
public void accept(ProgressNotification notification) {
if (notification == null) {
throw new IllegalArgumentException("Notification must not be null");
}View on GitHub (pinned to 98a7beda4f)
Solutions
- Change the method's return type to void.
- If a reactive implementation is needed, register with AsyncMcpProgressMethodCallback and return Mono<Void> instead.
- Expose testability via side effects (record state to a field/mock) rather than a return value.
Example fix
// before
@McpProgress
public boolean onProgress(Double p, String token) { return true; }
// after
@McpProgress
public void onProgress(Double p, String token) { /* handle */ } Defensive patterns
Strategy: type-guard
Validate before calling
if (!void.class.equals(method.getReturnType())) {
throw new IllegalStateException(method + " must return void for sync progress callback");
} Type guard
boolean isValidSyncProgressReturn(Method m) {
return void.class.equals(m.getReturnType());
} Try / catch
try {
syncRegistry.register(SyncMcpProgressMethodCallback.builder().method(m).bean(b).build());
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Synchronous progress methods must return void")) {
throw new ConfigurationException("Use void (sync) or Mono<Void> with the async callback", e);
}
throw e;
} Prevention
- Pick the sync vs async callback based on the handler's return type, not the other way around.
- Keep @McpProgress handlers void for simple synchronous handling.
- Add a startup test that builds all sync callbacks to catch signature drift.
When it happens
Trigger: Registering SyncMcpProgressMethodCallback with a method that returns boolean, Double, String, or Mono — anything other than void/primitive void — e.g. a handler written as `public boolean onProgress(Double p, String token)`.
Common situations: Using the sync callback with a handler originally written for the async API (returns Mono<Void>); handlers that return a status flag out of habit; refactoring that changed void to a return type for testability.
Related errors
- Mono return type must be Mono<Void>: {method.getName()} in {
- Asynchronous progress methods must return void or Mono<Void>
- Expected Mono<Void> but got Mono<
- Method must have void or Mono<Void> return type:
- Method must have void return type:
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/6618127399eb451a.
Report an issue: GitHub.