spring-projects/spring-ai · error · ClassCastException
Expected Mono<Void> but got Mono<
Error message
Expected Mono<Void> but got Mono<
What it means
Async resource-list-changed callbacks may return Mono<Void>; at invocation time the library flattens the returned Mono and throws a ClassCastException if the emitted value is non-null, meaning the Mono was Mono<SomethingElse> rather than Mono<Void>. The message names the observed value's class.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/changed/resource/AsyncMcpResourceListChangedMethodCallback.java:82
// Build arguments for the method call
Object[] args = this.buildArgs(this.method, null, updatedResources);
// Invoke the method
this.method.setAccessible(true);
Object result = this.method.invoke(this.bean, args);
// If the method returns a Mono, handle it
if (result instanceof Mono) {
// We need to handle the case where the Mono is not a Mono<Void>
// This is expected by the test testInvalidMonoReturnType
Mono<?> monoResult = (Mono<?>) result;
// Convert the Mono to a Mono<Void> by checking the value
// If the value is not null (i.e., not Void), throw a ClassCastException
return monoResult.flatMap(value -> {
if (value != null) {
// This will be caught by the test testInvalidMonoReturnType
throw new ClassCastException(
"Expected Mono<Void> but got Mono<" + value.getClass().getName() + ">");
}
return Mono.empty();
}).then();
}
// If the method returns void, return an empty Mono
return Mono.empty();
}
catch (Exception e) {
return Mono.error(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 validateView on GitHub (pinned to 98a7beda4f)
Solutions
- Convert the reactive chain to Mono<Void> by appending .then() (or .then(Mono.empty())).
- Replace Mono.just(value) returns with Mono.empty().
- If a value must be produced, change the method to void return type and handle the value internally.
Example fix
// before
public Mono<String> onResourcesChanged(List<McpSchema.Resource> r) { return doWork(r); }
// after
public Mono<Void> onResourcesChanged(List<McpSchema.Resource> r) { return doWork(r).then(); } Defensive patterns
Strategy: validation
Validate before calling
Method m = bean.getClass().getMethod("onResourcesChanged", List.class);
if (!Mono.class.isAssignableFrom(m.getReturnType())) throw new IllegalStateException("async callback must return Mono<Void>"); Type guard
static boolean returnsMonoVoid(Method m) {
return Mono.class.isAssignableFrom(m.getReturnType());
} Try / catch
try {
callback.accept(resources).block();
} catch (ClassCastException e) {
// Mono emitted a non-Void value; fix the method to end with .then()
log.error("Async callback returned Mono<T> instead of Mono<Void>: {}", e.getMessage());
} Prevention
- End every reactive callback chain with .then().
- Never return Mono.just(value) from a Mono<Void> method.
- Declare the return type as Mono<Void> so the compiler enforces it.
When it happens
Trigger: An async callback method returns Mono<String>/Mono<Boolean>/Mono<List<...>>; the value emitted at runtime is non-null inside flatMap, triggering the ClassCastException.
Common situations: Returning Mono.just("done") or a service call that returns typed Monos instead of chaining .then() or Mono.empty() to convert to Mono<Void>.
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
- Expected Mono<Void> but got Mono<
- Method must have void or Mono<Void> return type:
- Expected Mono<Void> but got Mono<" + value.getClass().getNam
- Method must return Mono<ElicitResult> or Mono<StructuredElic
- Expected Mono<Void> but got Mono<
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/a483fb9632b32aa8.
Report an issue: GitHub.