| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
sendMessage waited on inboundReady and outboundReady (two Sinks.One<Void>) via Mono.zip, but Mono.zip completes as soon as the first of two value-less sources completes -- it never actually waits for the second. Mono.when is the correct operator for waiting on multiple completion-only signals, and does wait for both. Fixes modelcontextprotocol#303
Pins down why Mono.when is the correct operator for the sendMessage readiness barrier: verifies Mono.zip incorrectly proceeds after only one of two Mono<Void> signals fires, while Mono.when correctly waits for both.
| Back | FazBrowse Home | New Git URL |
Summary
Fixes #303.
StdioMcpSessionTransport#sendMessage gates the actual write on inboundReady and outboundReady (two Sinks.One<Void>) both completing before proceeding:
Mono.zip is the wrong operator here. It's built to combine values, and it completes (empty) as soon as the first of the zipped sources completes without a value — it does not wait for the others. Since inboundReady/outboundReady are Mono<Void> (they only ever complete, never emit a value via tryEmitValue(null)), Mono.zip here only ever waits for whichever of the two signals fires first, silently breaking the intended "wait for both" barrier.
I verified this empirically before touching anything (both with tryEmitEmpty() and with the exact tryEmitValue(null) this code uses):
Mono.when is the correct operator for waiting on multiple completion-only signals — it waits for all of them regardless of whether they emit a value.
Fix
One-line change: Mono.zip(...) → Mono.when(...).
Test
Added monoZipDoesNotWaitForBothVoidSignals_monoWhenDoes to StdioServerTransportProviderTests, which reproduces the exact combinator pattern used by sendMessage and asserts:
I want to be upfront about scope: this is a targeted unit test of the reactive operator semantics, not a full black-box integration test driving the real threading race through sendMessage end-to-end — inboundReady/outboundReady are private fields on a private inner class with no injectable scheduler, so reproducing the exact race deterministically through the public API would need either reflection or new test seams, which felt like more than a one-line fix warrants. Happy to extend it if a maintainer would rather see that.
Verification
Related PRs
A few prior PRs proposed the identical one-line fix but appear to have gone stale without review: #846, #981, #987. This PR adds a regression test on top of the same fix.