{"record":{"id":"1edf79bb156a9143","repo":"spring-projects/spring-framework","slug":"not-a-reactive-streams-publisher","errorCode":null,"errorMessage":"Not a Reactive Streams 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/69bf83ad716d0cfc4b0520a19b4d8b24c79d1538/spring-aop/src/main/java/org/springframework/aop/framework/CoroutinesUtils.java#L21-L50","documentation":"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).","triggerScenarios":"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).","commonSituations":"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.","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)"],"exampleFix":"// before\nsuspend fun flow(): Flow<Int> = error(\"...\")\n// advice returns null -> 'Not a Reactive Streams Publisher: null'\n\n// after\n// make the function return an actual Publisher-backed Flow,\n// and have advice return an empty publisher: Flux.empty()","handlingStrategy":"type-guard","validationCode":"// Before converting, ensure the value is a Publisher:\nif (!(value instanceof Publisher)) {\n  throw new IllegalStateException(\"Expected a Reactive Streams Publisher, got: \" + value);\n}","typeGuard":"private static boolean isPublisher(Object v) {\n  return v instanceof org.reactivestreams.Publisher;\n}","tryCatchPattern":null,"preventionTips":["Make Kotlin suspend Flow functions return a Publisher-backed value","Do not let advice substitute null for a Flow return type"],"tags":["spring-aop","kotlin","coroutines","reactive-streams"],"backgroundTag":null,"analyzedSha":"69bf83ad716d0cfc4b0520a19b4d8b24c79d1538","analyzedAt":"2026-08-09T15:32:58.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}