Complete empty Uni instances from the Mutiny reactive adapter - #37259
Merged
bclozel merged 1 commit intoSep 9, 2026
Merged
Conversation
The Mutiny Uni adapter registers its empty-value supplier as Uni.createFrom().nothing(), which returns a Uni that never signals an item, a failure, or completion. Every sibling registration supplies an empty value that completes immediately: Mono.empty(), Maybe.empty(), Completable.complete(), and CompletableDeferred(null); the Multi registration uses Multi.createFrom().empty() as well. ReactiveAdapter.toPublisher(null) substitutes that empty value whenever a null source needs to be adapted, for example when a WebFlux handler method with a Uni return type returns null. With a never-completing empty value the resulting Publisher emits no signal at all, so the response is never written and the request hangs until a timeout, whereas the same handler declared with Mono completes empty. The adapter also becomes asymmetric with its own fromPublisher function, which adapts an empty Publisher to a Uni that completes with a null item. The supplier now uses Uni.createFrom().nullItem(), whose conversion to a Publisher completes without emitting an item, matching the sibling adapters and the round-trip through fromPublisher. The descriptor is shared by the Mutiny 1 and Mutiny 2 registrations, so both paths are covered. Signed-off-by: junhyeong9812 <pickjog@gmail.com>
bclozel
force-pushed
the
fix/mutiny-uni-empty-value
branch
from
September 9, 2026 12:47
c572158 to
d3d8e05
Compare
Member
|
Thanks @junhyeong9812 ! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
The Mutiny
Uniadapter inReactiveAdapterRegistrysuppliesUni.createFrom().nothing()as its empty value. ThatUninever signals, so everywhere the framework adapts anullsource - most visibly a WebFlux handler method declared to returnUni<T>that returnsnull- the resultingPublishernever completes and the request hangs, while the same handler declared withMono<T>completes empty. This PR changes the supplier toUni.createFrom().nullItem().Problem
ReactiveAdapter.toPublisher(Object)substitutesgetDescriptor().getEmptyValue()when the source isnull. TheUnidescriptor is registered withReactiveTypeDescriptor.singleOptionalValue, which declares that the type supports an empty value, but the supplied empty value isUni.createFrom().nothing()- aUnithat never emits an item, a failure, or completion. Adapting it produces aPublisherthat never signals, so a blocking or subscribing consumer waits until its own timeout.All sibling registrations supply an empty value that completes immediately:
Mono::empty,Maybe::empty,Completable::complete,CompletableDeferred(null), and theMultiregistration itself usesMulti.createFrom().empty(). TheUniregistration is also asymmetric with its ownfromPublisherfunction: adapting an emptyPublisherproduces aUnithat completes with anullitem, but the registered empty value cannot make the trip back.Fix
The empty-value supplier now uses
Uni.createFrom().nullItem(). Mutiny converts aUniwith anullitem to aPublisherthat completes without emitting an item (Reactive Streams forbidsonNext(null)), which matches the sibling adapters and makes the round trip throughfromPublishersymmetric. The descriptor instance is shared by the Mutiny 1 and Mutiny 2 registrations, so both are covered.Two tests are added: adapting a
nullsource completes empty within a timeout (previously it hung), and adapting an emptyPublishertoUniyields anullitem, pinning the round-trip contract. The fullspring-coretest suite passes.