-
Type:
Bug
-
Resolution: Unresolved
-
Priority:
Unknown
-
None
-
Affects Version/s: 5.6.5
-
Component/s: Reactive Streams
-
None
-
None
-
Java Drivers
-
None
-
None
-
None
-
None
-
None
-
None
Summary
A write completion handler in AsynchronousChannelStream.pipeOneBuffer calls byteBuffer.hasRemaining() after the buffer has been released. ByteBufNIO.release() sets buf = null when the reference count reaches zero, so the call throws a NullPointerException on the channel group thread. The handler is the root completion handler, so the exception is uncaught and the outer handler is never completed.
Driver: mongodb-driver-reactivestreams 5.6.5 (same code in 5.14.0). Server: MongoDB 7.0.34, two-node replica set. JDK 25 (Eclipse Temurin 25.0.4 and IBM Semeru 25.0.4), Linux x86_64.
Exception in thread "InnocuousThread-10" java.lang.NullPointerException: Cannot invoke "java.nio.ByteBuffer.hasRemaining()" because "this.buf" is null at org.bson.ByteBufNIO.hasRemaining(ByteBufNIO.java:91) at com.mongodb.internal.connection.AsynchronousChannelStream$2.completed(AsynchronousChannelStream.java:177) at com.mongodb.internal.connection.AsynchronousChannelStream$2.completed(AsynchronousChannelStream.java:174) at com.mongodb.connection.AsyncCompletionHandler.lambda$asCallback$0(AsyncCompletionHandler.java:51) at com.mongodb.internal.async.AsyncSupplier.lambda$finish$0(AsyncSupplier.java:73) at com.mongodb.internal.async.AsyncSupplier.lambda$finish$0(AsyncSupplier.java:73) at com.mongodb.internal.async.SingleResultCallback$1.completed(SingleResultCallback.java:49) at com.mongodb.internal.connection.AsynchronousChannelStream$AsyncWritableByteChannelAdapter$WriteCompletionHandler.completed(AsynchronousChannelStream.java:209) at com.mongodb.internal.connection.AsynchronousChannelStream$AsyncWritableByteChannelAdapter$WriteCompletionHandler.completed(AsynchronousChannelStream.java:200) at java.base/sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:121) at java.base/sun.nio.ch.Invoker$1.run(Invoker.java:201) at java.base/sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:108) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) at java.base/java.lang.Thread.run(Thread.java:1474) at java.base/jdk.internal.misc.InnocuousThread.run(InnocuousThread.java:148)
On OpenJ9 the first line reads Cannot read field "buf" because "this" is null; that is an OpenJ9 message bug (reported as https://github.com/eclipse-openj9/openj9/issues/25159), the frames are identical.
Where it happens
It happens when a fresh two-node replica set is initiated and the first client connects while the nodes elect a primary, so connections are being dropped while the driver writes to them. In AsynchronousChannelStream.java (5.6.5) the completion handler created in pipeOneBuffer (line 174) calls byteBuffer.hasRemaining() (line 177) when the channel write completes. If the message buffers were released between the write starting and its completion, ByteBufNIO.buf is already null (ByteBufNIO.java lines 57 to 65). In 5.14.0 the same check is thenRunWhileLoop(byteBuffer::hasRemaining, ...) at line 199 and release() is unchanged, so the window appears to still exist.
Frequency
In the Quarkus project's CI, scheduled builds of main from 2026-07-28 to 2026-10-01, the exception appears in 3 of 87 runs on Temurin 25 and 11 of 86 runs on Semeru 25 of the quarkus-mongodb-client runtime tests. Console logs: https://develocity.quarkus.io/s/3e2vm34x7tce4/console-log (Temurin, 2026-08-08) and https://develocity.quarkus.io/s/yw432zawqv4pc/console-log (Semeru, 2026-09-01). The tests themselves pass; the exception is only visible as an uncaught exception on the driver thread.
Steps to reproduce
git clone --depth 1 https://github.com/quarkusio/quarkus.git cd quarkus ./mvnw -pl extensions/mongodb-client/runtime test -Dtest=ListDatabaseTest -Dsurefire.failIfNoSpecifiedTests=false
ListDatabaseTest starts a two-node MongoDB 7.0 replica set on ports 27018 and 27019 with flapdoodle, then opens a reactive streams client and lists databases. Repeat the command and look for Exception in thread "InnocuousThread in the output. Locally the rate is low on an idle 8-core machine: 1 of 22 runs on JDK 25 (HotSpot 25.0.4 and Semeru 25.0.4), the hit being the first, slowest run when mongod started cold. Pinned to two cores with taskset -c 0,1 and running the whole module (./mvnw -pl extensions/mongodb-client/runtime test), Semeru 25.0.4 hit it in 1 of 6 runs, that time during ConnectionToReplicaSetTest, the other test in the module that starts a replica set. 0 of 5 runs with the driver overridden to 5.14.0, which says nothing at that rate. CI's 4-core runners hit it in 3% to 13% of runs, so load widens the window. I can reduce this to a standalone program without Quarkus if that helps.
Expected
No uncaught exception on the channel group thread: either the write loop holds its own reference to the buffer until the last completion has run, or the completion handler treats a released buffer as "nothing remaining" and completes the outer handler.