spring-projects/spring-framework · error · IllegalArgumentException
Not a Reactive Streams Publisher: {}
Error message
Not a Reactive Streams Publisher: {} What it means
CoroutinesUtils.asFlow() (line 34) is invoked from CglibAopProxy.processReturnType() when a Kotlin suspending function returns a Flow and the returned object needs converting to kotlinx.coroutines.flow.Flow via ReactiveFlowKt.asFlow(Publisher). That conversion requires a Reactive Streams Publisher; anything else is rejected (line 39).
Source
Thrown at spring-aop/src/main/java/org/springframework/aop/framework/CoroutinesUtils.java:39
import kotlinx.coroutines.reactor.MonoKt;
import org.jspecify.annotations.Nullable;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
/**
* Package-visible class designed to avoid a hard dependency on Kotlin and Coroutines dependency at runtime.
*
* @author Sebastien Deleuze
* @since 6.1
*/
abstract class CoroutinesUtils {
static Object asFlow(@Nullable Object publisher) {
if (publisher instanceof Publisher<?> rsPublisher) {
return ReactiveFlowKt.asFlow(rsPublisher);
}
else {
throw new IllegalArgumentException("Not a Reactive Streams Publisher: " + publisher);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
static @Nullable Object awaitSingleOrNull(@Nullable Object value, Object continuation) {
return MonoKt.awaitSingleOrNull(value instanceof Mono mono ? mono : Mono.justOrEmpty(value),
(Continuation<Object>) continuation);
}
}
View on GitHub (pinned to 69bf83ad71)
Solutions
- Ensure the proxied suspending function actually returns a Reactive Streams Publisher (e.g. a Flux/Mono) that asFlow can convert
- Do not let advice return null for a Flow-returning suspending function
- If returning null is valid, handle it before the proxy's processReturnType path (return an empty Flow/Publisher instead)
Example fix
// before
suspend fun flow(): Flow<Int> = error("...")
// advice returns null -> 'Not a Reactive Streams Publisher: null'
// after
// make the function return an actual Publisher-backed Flow,
// and have advice return an empty publisher: Flux.empty() Defensive patterns
Strategy: type-guard
Validate before calling
// Before converting, ensure the value is a Publisher:
if (!(value instanceof Publisher)) {
throw new IllegalStateException("Expected a Reactive Streams Publisher, got: " + value);
} Type guard
private static boolean isPublisher(Object v) {
return v instanceof org.reactivestreams.Publisher;
} Prevention
- Make Kotlin suspend Flow functions return a Publisher-backed value
- Do not let advice substitute null for a Flow return type
When it happens
Trigger: A Kotlin suspending function (proxied by Spring AOP) whose last parameter type is kotlinx.coroutines.flow.Flow returns a value that is not a Reactive Streams Publisher (e.g. null, a plain object, a Mono handled on the wrong branch).
Common situations: A Kotlin suspend function returning a Flow whose underlying implementation returns a non-Publisher (or null) due to a bug or an unexpected return type; mismatch between the declared Flow return and what the advice/target actually returns; a custom advice that substitutes a non-Publisher value.
Related errors
- no suitable constructor or factory method found
- MethodInvocation is not a Spring ProxyMethodInvocation: {}
- 'argumentNames' property of AbstractAspectJAdvice contains a
- Only afterReturning advice can be used to bind a return valu
- Returning name '{}' is neither a valid argument name nor the
AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09).
Data as JSON: /api/errors/1edf79bb156a9143.
Report an issue: GitHub.