spring-projects/spring-ai · error

Method must have void return type: " + method.getName() + "

Error message

Method must have void return type: " + method.getName() + " in " + method.getDeclaringClass().getName() + " returns " + returnType.getName()

What it means

Sync resource-list-changed callback methods must return void. This IllegalArgumentException is thrown at registration by validateReturnType when the method returns any other type. Sync callbacks cannot report results, so non-void returns are rejected.

Source

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

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

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

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Change the method's return type to void for the sync callback.
  2. If reactive handling is needed, register the method with the async callback (Mono<Void> allowed) instead.
  3. Move side-effect results into internal fields/logging rather than the return value.

Example fix

// before
public boolean onResourcesChanged(List<McpSchema.Resource> r) { ...; return true; }
// after
public void onResourcesChanged(List<McpSchema.Resource> r) { ... }
Defensive patterns

Strategy: validation

Validate before calling

if (m.getReturnType() != void.class) throw new IllegalStateException("sync resource-list-changed callback must return void");

Type guard

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

Try / catch

try {
    registry.register(syncCallback);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Method must have void return type")) {
        throw new IllegalStateException("Sync callback must be declared void", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Registering a sync (SyncMcpResourceListChangedMethodCallback) callback whose return type is boolean, String, Mono, or any other non-void type.

Common situations: Sharing one handler method between sync and async registrations (async needs Mono<Void>), or returning a status value the developer expects the framework to log.

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