{"id":"9418fb359e7a41c7","repo":"spring-projects/spring-framework","slug":"not-a-reactive-streams-publisher-publisher","errorCode":null,"errorMessage":"Not a Reactive Streams Publisher: {publisher}","messagePattern":"Not a Reactive Streams Publisher: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spring-aop/src/main/java/org/springframework/aop/framework/CoroutinesUtils.java","lineNumber":39,"sourceCode":"import kotlinx.coroutines.reactor.MonoKt;\nimport org.jspecify.annotations.Nullable;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Mono;\n\n/**\n * Package-visible class designed to avoid a hard dependency on Kotlin and Coroutines dependency at runtime.\n *\n * @author Sebastien Deleuze\n * @since 6.1\n */\nabstract class CoroutinesUtils {\n\n\tstatic Object asFlow(@Nullable Object publisher) {\n\t\tif (publisher instanceof Publisher<?> rsPublisher) {\n\t\t\treturn ReactiveFlowKt.asFlow(rsPublisher);\n\t\t}\n\t\telse {\n\t\t\tthrow new IllegalArgumentException(\"Not a Reactive Streams Publisher: \" + publisher);\n\t\t}\n\t}\n\n\t@SuppressWarnings({\"rawtypes\", \"unchecked\"})\n\tstatic @Nullable Object awaitSingleOrNull(@Nullable Object value, Object continuation) {\n\t\treturn MonoKt.awaitSingleOrNull(value instanceof Mono mono ? mono : Mono.justOrEmpty(value),\n\t\t\t\t(Continuation<Object>) continuation);\n\t}\n\n}\n","sourceCodeStart":21,"sourceCodeEnd":50,"githubUrl":"https://github.com/spring-projects/spring-framework/blob/e8729d043887bf0d0baf91e062e909b56eb2b708/spring-aop/src/main/java/org/springframework/aop/framework/CoroutinesUtils.java#L21-L50","documentation":"Thrown by CoroutinesUtils.asFlow (package-private, invoked by both CglibAopProxy.processReturnType and JdkDynamicAopProxy.invoke) when the return value of a Kotlin suspending function expected to produce a Flow is not a Reactive Streams Publisher. Spring's Kotlin coroutine bridge converts Publisher to a Kotlin Flow via ReactiveFlowKt.asFlow; passing anything else (a Mono for a non-flow path, a plain object, null-handled elsewhere) is treated as a contract violation. This is an internal invariant for the Flow-returning suspending-function branch.","triggerScenarios":"A Kotlin suspending function whose last parameter type is a Flow and whose returned value is not a Publisher (e.g. a coroutine framework returns a Deferred, a raw value, or an unexpected wrapper); AOP advice that replaces the return value of such a method with a non-Publisher; version mismatch between kotlinx-coroutines/reactor bindings producing unexpected types.","commonSituations":"Returning the wrong reactive type from advice wrapping a Flow-returning Kotlin suspending function; mixing Reactor/Kotlin coroutine versions; a custom Around interceptor that substitutes null or a scalar for a Flow position.","solutions":["Ensure the value returned for a Flow-returning Kotlin suspending function is a Reactive Streams Publisher (Flux/Mono from Reactor, or a kotlinx publisher).","If writing custom @Around advice for such methods, return the original proceed() result unchanged, or wrap your value in Flux.fromStream(...)/Mono.just(...) as appropriate.","Align kotlinx-coroutines-reactive / kotlinx-coroutines-reactor versions with Spring's expected 6.1+ contracts.","Avoid advising Flow-returning Kotlin functions with generic interceptors that do not understand coroutine return-type semantics."],"exampleFix":"// before\n@Around(\"kotlinSuspendFlowPointcut()\")\npublic Object around(ProceedingJoinPoint pjp) {\n  return List.of(1, 2, 3);  // not a Publisher -> throws in Flow branch\n}\n\n// after\n@Around(\"kotlinSuspendFlowPointcut()\")\npublic Object around(ProceedingJoinPoint pjp) throws Throwable {\n  return Flux.fromIterable(List.of(1, 2, 3));  // Reactive Streams Publisher\n}","handlingStrategy":"type-guard","validationCode":"Object ret = ...; // value to convert for a Flow-returning Kotlin suspending fn\nif (!(ret instanceof org.reactivestreams.Publisher<?>)) {\n  throw new IllegalArgumentException(\"Expected a Reactive Streams Publisher for Flow conversion, got \" + ret);\n}","typeGuard":"static boolean isPublisherForFlow(Object o) {\n  return o instanceof org.reactivestreams.Publisher<?>;\n}","tryCatchPattern":null,"preventionTips":["Return only Publisher (Flux/Mono) values from advice on Flow-returning Kotlin suspending functions.","Align kotlinx-coroutines-reactor version with Spring's expected contract.","Avoid generic interceptors on Kotlin suspending Flow methods unless they understand coroutine return semantics."],"tags":["spring-aop","kotlin","coroutines","reactive-streams","flow"],"analyzedSha":"e8729d043887bf0d0baf91e062e909b56eb2b708","analyzedAt":"2026-08-04T19:07:39.725Z","schemaVersion":2}