spring-projects/spring-ai · error · IllegalArgumentException

Method must have void return type:

Error message

Method must have void return type: 

What it means

SyncMcpLoggingMethodCallback.validateReturnType requires the annotated sync logging consumer method to return void; any other return type triggers this IllegalArgumentException at registration, naming the method, declaring class, and actual return type. Sync consumers are fire-and-forget and cannot return values to the framework.

Source

Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/logging/SyncMcpLoggingMethodCallback.java:83

		}
		catch (Exception e) {
			throw new McpLoggingConsumerMethodException(
					"Error invoking logging consumer method: " + this.method.getName(), e);
		}
	}

	/**
	 * Validates that the method return type is compatible with the logging 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 SyncMcpLoggingConsumerMethodCallback instances.
	 * <p>
	 * This builder provides a fluent API for constructing
	 * SyncMcpLoggingConsumerMethodCallback instances with the required parameters.
	 */

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Change the method to return void.
  2. If you need the async variant, use Mono<Void> with the async annotation/callback instead.
  3. Extract the logic into a void method and keep the value-returning method separate.
  4. Discard the return value explicitly by assigning to a local variable inside a void wrapper method.

Example fix

// before
public boolean handleLog(LoggingMessageNotification n) { log(n); return true; }
// after
public void handleLog(LoggingMessageNotification n) { log(n); }
Defensive patterns

Strategy: validation

Validate before calling

if (handlerMethod.getReturnType() != void.class) {
    throw new IllegalStateException("Sync @McpLogging handler must return void: " + handlerMethod);
}

Try / catch

try {
    registry.registerSyncLogging(bean, method);
} catch (IllegalArgumentException e) {
    logger.error("Sync logging handler must be void: {}", e.getMessage());
}

Prevention

When it happens

Trigger: Registering a method like boolean handleLog(LoggingMessageNotification n) or LoggingMessageNotification handleLog(...) with the sync MCP logging annotation; the callback constructor calls validateMethod and throws immediately.

Common situations: Reusing an existing method that returns a value as a logging consumer; porting an async Mono<Void> handler to the sync annotation without stripping the return; IDE auto-generated stubs returning Object.

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