Skip to content

Stabilize flaky test: McpCompletionTests.testCompletionErrorOnMissingContext #902

Open

Description

@ashakirin

McpCompletionTests.testCompletionErrorOnMissingContext is sometimes failing and recovers after simple build restart.
**Error:** Session not found: <sessionId>

Likely causes:
The server removes the session when SSE connection closes, but the client still has pending requests in flight. This is a race condition between:

  • SSE connection closing → server removes session
  • Client sending next request → "Session not found"
2026-02-09 23:31:09 [HttpClient-1-Worker-0] WARN  i.m.c.t.HttpClientSseClientTransport - SSE stream observed an error
java.io.IOException: chunked transfer encoding, state: READING_LENGTH
	at java.net.http/jdk.internal.net.http.common.Utils.wrapWithExtraDetail(Utils.java:375)
	at java.net.http/jdk.internal.net.http.Http1Response$BodyReader.onReadError(Http1Response.java:660)
	at java.net.http/jdk.internal.net.http.Http1AsyncReceiver.checkForErrors(Http1AsyncReceiver.java:302)
	at java.net.http/jdk.internal.net.http.Http1AsyncReceiver.flush(Http1AsyncReceiver.java:268)
	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$LockingRestartableTask.run(SequentialScheduler.java:205)
	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:149)
	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:230)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: java.io.EOFException: EOF reached while reading
	at java.net.http/jdk.internal.net.http.Http1AsyncReceiver$Http1TubeSubscriber.onComplete(Http1AsyncReceiver.java:597)
	at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$ReadSubscription.signalCompletion(SocketTube.java:648)
	at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$InternalReadSubscription.read(SocketTube.java:853)
	at java.net.http/jdk.internal.net.http.SocketTube$SocketFlowTask.run(SocketTube.java:181)
	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:230)
	at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:303)
	at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:256)
	at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$InternalReadSubscription.signalReadable(SocketTube.java:782)
	at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$ReadEvent.signalEvent(SocketTube.java:965)
	at java.net.http/jdk.internal.net.http.SocketTube$SocketFlowEvent.handle(SocketTube.java:253)
	at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.handleEvent(HttpClientImpl.java:1402)
	at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.lambda$run$3(HttpClientImpl.java:1347)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.run(HttpClientImpl.java:1347)
2026-02-09 23:31:09 [ForkJoinPool.commonPool-worker-1] ERROR i.m.c.t.HttpClientSseClientTransport - Error sending message: Sending message failed with a non-OK HTTP code: 404 - {"cause":null,"jsonRpcError":null,"localizedMessage":"Session not found: 7a9438d6-5965-456a-99af-26523f108d68","message":"Session not found: 7a9438d6-5965-456a-99af-26523f108d68","stackTrace":

https://github.com/modelcontextprotocol/java-sdk/actions/runs/21845041347/job/63038831474#step:4:15277

Activity

  1. added a commit that references this issue on Apr 4, 2026
    24587e3
  2. rsmihir389 commented on Apr 4, 2026

    @rsmihir389

    Hey — we have been looking at exactly this kind of failure pattern a lot lately. Race conditions between SSE teardown and in-flight client requests like this usually cost teams more than just the failed test — every rerun burns CI minutes while the underlying race stays unfixed.

    A few things that tend to help with this specific pattern:

    1. Add a small drain period before the server removes the session — give in-flight requests a window (50–100ms) to complete or time out before the session map is cleaned up
    2. Retry with backoff on the client side for 404s on session endpoints specifically, since these are expected during teardown races
    3. Use a latch or countdown in the test itself to ensure the client has finished sending before the SSE connection is allowed to close

    We built a tool that detects flaky tests like this and shows the dollar cost in CI time: kleore.com. Here is what a report looks like on a real repo: sample report.

    60-second install as a GitHub App, free tier shows the cost. Might be useful for catching other intermittent failures in this repo before they pile up.

  3. however-yir commented on Apr 10, 2026

    @however-yir

    I investigated McpCompletionTests.testCompletionErrorOnMissingContext flakiness.

    Likely cause:

    • asynchronous timing race between completion setup and error assertion.

    Proposed stabilization:

    • replace timing-sensitive assertion with deterministic synchronization (await/latch/future completion)
    • assert exact exception type/message once async pipeline completes
    • avoid sleep-based waiting

    If maintainers agree, I can submit a focused PR with only test reliability changes.

  4. Kehrlann commented on Apr 10, 2026

    @Kehrlann
    Contributor

    @however-yir Before we submit a fix, I'd like to reliably reproduce the issue.

    If this is a race condition, it should be possible to add an artificial delay somewhere to make the test fail consistently. We wouldn't commit that delay, just demonstrate the issue. Then we can think of a fix.

    ftr there is some more discussions happening here in #903

  5. added
    testsAddresses tests only
    P3Nice to haves, rare edge cases
    needs reproWe need a way to reproduce the problem
    on Apr 10, 2026
  6. ashakirin commented on Apr 10, 2026

    @ashakirin
    ContributorAuthor

    @however-yir I currently working to reproduce it reliably in separate test, will share the branch - fill free to contribute

  7. added 2 commits that reference this issue on Apr 13, 2026
    b228ff4
    086343a
  8. ashakirin commented on Apr 13, 2026

    @ashakirin
    ContributorAuthor

    @however-yir, @Kehrlann: I added McpCompletionFlakyReproductionTest, likely reproducing the flaky situation happens on CI/CD into fix branch. Could you please have a look?
    Artificial connection break in lines 85-90 causes:

    1. [boundedElastic-1] Failed to send message to session: ...ob is null (= Client disconnected)
    2. [http-nio-exec-4] Error processing message: AsyncContext already completed
    3. [HttpClient-Worker] SSE stream observed an error + chunked transfer encoding + EOFException
    4. [ForkJoinPool-worker] 500 - AsyncContext already completed
    5. [ForkJoinPool-worker] 404 - Session not found

    Would you like to take a look?

  9. Kehrlann commented on Apr 14, 2026

    @Kehrlann
    Contributor

    Spoke with @ashakirin today. It's unclear which test actually flakes mot often.

    Let's start tracking the flakes in this particular issue until we see a pattern emerging.
    Example: https://github.com/modelcontextprotocol/java-sdk/actions/runs/24340206510/job/71066978434#step:4:50240

    Stacktrace:

    11:10:55.474 [http-nio-40611-exec-4] DEBUG io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider -- Handling GET request for session: d54ef607-967f-4c42-b930-3842d36eaa87
    11:10:55.474 [http-nio-40611-exec-4] DEBUG io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider -- Streamable session transport d54ef607-967f-4c42-b930-3842d36eaa87 initialized with SSE writer
    11:10:55.475 [HttpClient-61-Worker-1] DEBUG io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport -- Received response, using string subscriber
    11:10:55.476 [HttpClient-61-Worker-1] DEBUG io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport -- Handling exception for session d54ef607-967f-4c42-b930-3842d36eaa87
    java.lang.RuntimeException: Failed to send message: AggregateResponseEvent[responseInfo=jdk.internal.net.http.ResponseInfoImpl@697fc5c4, data={"cause":null,"stackTrace":[{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"build","fileName":"McpError.java","lineNumber":72,"nativeMethod":false,"className":"io.modelcontextprotocol.spec.McpError$Builder"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"doPost","fileName":"HttpServletStreamableServerTransportProvider.java","lineNumber":559,"nativeMethod":false,"className":"io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"service","fileName":"HttpServlet.java","lineNumber":645,"nativeMethod":false,"className":"jakarta.servlet.http.HttpServlet"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"service","fileName":"HttpServlet.java","lineNumber":716,"nativeMethod":false,"className":"jakarta.servlet.http.HttpServlet"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"doFilter","fileName":"ApplicationFilterChain.java","lineNumber":130,"nativeMethod":false,"className":"org.apache.catalina.core.ApplicationFilterChain"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"StandardWrapperValve.java","lineNumber":167,"nativeMethod":false,"className":"org.apache.catalina.core.StandardWrapperValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"StandardContextValve.java","lineNumber":79,"nativeMethod":false,"className":"org.apache.catalina.core.StandardContextValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"AuthenticatorBase.java","lineNumber":483,"nativeMethod":false,"className":"org.apache.catalina.authenticator.AuthenticatorBase"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"StandardHostValve.java","lineNumber":115,"nativeMethod":false,"className":"org.apache.catalina.core.StandardHostValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"ErrorReportValve.java","lineNumber":93,"nativeMethod":false,"className":"org.apache.catalina.valves.ErrorReportValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"StandardEngineValve.java","lineNumber":74,"nativeMethod":false,"className":"org.apache.catalina.core.StandardEngineValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"service","fileName":"CoyoteAdapter.java","lineNumber":342,"nativeMethod":false,"className":"org.apache.catalina.connector.CoyoteAdapter"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"service","fileName":"Http11Processor.java","lineNumber":395,"nativeMethod":false,"className":"org.apache.coyote.http11.Http11Processor"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"process","fileName":"AbstractProcessorLight.java","lineNumber":63,"nativeMethod":false,"className":"org.apache.coyote.AbstractProcessorLight"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"process","fileName":"AbstractProtocol.java","lineNumber":905,"nativeMethod":false,"className":"org.apache.coyote.AbstractProtocol$ConnectionHandler"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"doRun","fileName":"NioEndpoint.java","lineNumber":1746,"nativeMethod":false,"className":"org.apache.tomcat.util.net.NioEndpoint$SocketProcessor"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"run","fileName":"SocketProcessorBase.java","lineNumber":52,"nativeMethod":false,"className":"org.apache.tomcat.util.net.SocketProcessorBase"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"runWorker","fileName":"ThreadPoolExecutor.java","lineNumber":1148,"nativeMethod":false,"className":"org.apache.tomcat.util.threads.ThreadPoolExecutor"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"run","fileName":"ThreadPoolExecutor.java","lineNumber":636,"nativeMethod":false,"className":"org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"run","fileName":"TaskThread.java","lineNumber":63,"nativeMethod":false,"className":"org.apache.tomcat.util.threads.TaskThread$WrappingRunnable"},{"classLoaderName":null,"moduleName":"java.base","moduleVersion":"17.0.18","methodName":"run","fileName":"Thread.java","lineNumber":840,"nativeMethod":false,"className":"java.lang.Thread"}],"jsonRpcError":{"code":-32603,"message":"Error processing message: Stream unavailable for session d54ef607-967f-4c42-b930-3842d36eaa87"},"message":"Error processing message: Stream unavailable for session d54ef607-967f-4c42-b930-3842d36eaa87","suppressed":[],"localizedMessage":"Error processing message: Stream unavailable for session d54ef607-967f-4c42-b930-3842d36eaa87"}
    ]
    	at io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport.lambda$sendMessage$30(HttpClientStreamableHttpTransport.java:635)
    	at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:388)
    	at reactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onNext(MonoFlatMapMany.java:251)
    	at reactor.core.publisher.FluxCreate$BufferAsyncSink.drain(FluxCreate.java:880)
    	at reactor.core.publisher.FluxCreate$BufferAsyncSink.next(FluxCreate.java:805)
    	at reactor.core.publisher.FluxCreate$SerializedFluxSink.next(FluxCreate.java:163)
    	at io.modelcontextprotocol.client.transport.ResponseSubscribers$AggregateSubscriber.hookOnComplete(ResponseSubscribers.java:263)
    	at reactor.core.publisher.BaseSubscriber.onComplete(BaseSubscriber.java:197)
    	at org.reactivestreams.FlowAdapters$FlowToReactiveSubscriber.onComplete(FlowAdapters.java:221)
    	at java.net.http/jdk.internal.net.http.LineSubscriberAdapter$LineSubscription.loop(LineSubscriberAdapter.java:430)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$LockingRestartableTask.run(SequentialScheduler.java:205)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:149)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:230)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:303)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:256)
    	at java.net.http/jdk.internal.net.http.LineSubscriberAdapter$LineSubscription.signalComplete(LineSubscriberAdapter.java:193)
    	at java.net.http/jdk.internal.net.http.LineSubscriberAdapter.onComplete(LineSubscriberAdapter.java:114)
    	at java.net.http/jdk.internal.net.http.common.HttpBodySubscriberWrapper.complete(HttpBodySubscriberWrapper.java:293)
    	at java.net.http/jdk.internal.net.http.common.HttpBodySubscriberWrapper.onComplete(HttpBodySubscriberWrapper.java:401)
    	at java.net.http/jdk.internal.net.http.ResponseContent$ChunkedBodyParser.accept(ResponseContent.java:219)
    	at java.net.http/jdk.internal.net.http.ResponseContent$ChunkedBodyParser.accept(ResponseContent.java:130)
    	at java.net.http/jdk.internal.net.http.Http1Response$BodyReader.handle(Http1Response.java:691)
    	at java.net.http/jdk.internal.net.http.Http1Response$BodyReader.handle(Http1Response.java:619)
    	at java.net.http/jdk.internal.net.http.Http1Response$Receiver.accept(Http1Response.java:511)
    	at java.net.http/jdk.internal.net.http.Http1Response$BodyReader.tryAsyncReceive(Http1Response.java:649)
    	at java.net.http/jdk.internal.net.http.Http1AsyncReceiver.flush(Http1AsyncReceiver.java:233)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$LockingRestartableTask.run(SequentialScheduler.java:205)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:149)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$TryEndDeferredCompleter.complete(SequentialScheduler.java:347)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:151)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:230)
    	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    	at java.base/java.lang.Thread.run(Thread.java:840)
    11:10:55.476 [HttpClient-61-Worker-1] WARN io.modelcontextprotocol.client.LifecycleInitializer -- Handling exception
    java.lang.RuntimeException: Failed to send message: AggregateResponseEvent[responseInfo=jdk.internal.net.http.ResponseInfoImpl@697fc5c4, data={"cause":null,"stackTrace":[{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"build","fileName":"McpError.java","lineNumber":72,"nativeMethod":false,"className":"io.modelcontextprotocol.spec.McpError$Builder"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"doPost","fileName":"HttpServletStreamableServerTransportProvider.java","lineNumber":559,"nativeMethod":false,"className":"io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"service","fileName":"HttpServlet.java","lineNumber":645,"nativeMethod":false,"className":"jakarta.servlet.http.HttpServlet"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"service","fileName":"HttpServlet.java","lineNumber":716,"nativeMethod":false,"className":"jakarta.servlet.http.HttpServlet"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"doFilter","fileName":"ApplicationFilterChain.java","lineNumber":130,"nativeMethod":false,"className":"org.apache.catalina.core.ApplicationFilterChain"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"StandardWrapperValve.java","lineNumber":167,"nativeMethod":false,"className":"org.apache.catalina.core.StandardWrapperValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"StandardContextValve.java","lineNumber":79,"nativeMethod":false,"className":"org.apache.catalina.core.StandardContextValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"AuthenticatorBase.java","lineNumber":483,"nativeMethod":false,"className":"org.apache.catalina.authenticator.AuthenticatorBase"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"StandardHostValve.java","lineNumber":115,"nativeMethod":false,"className":"org.apache.catalina.core.StandardHostValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"ErrorReportValve.java","lineNumber":93,"nativeMethod":false,"className":"org.apache.catalina.valves.ErrorReportValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"StandardEngineValve.java","lineNumber":74,"nativeMethod":false,"className":"org.apache.catalina.core.StandardEngineValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"service","fileName":"CoyoteAdapter.java","lineNumber":342,"nativeMethod":false,"className":"org.apache.catalina.connector.CoyoteAdapter"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"service","fileName":"Http11Processor.java","lineNumber":395,"nativeMethod":false,"className":"org.apache.coyote.http11.Http11Processor"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"process","fileName":"AbstractProcessorLight.java","lineNumber":63,"nativeMethod":false,"className":"org.apache.coyote.AbstractProcessorLight"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"process","fileName":"AbstractProtocol.java","lineNumber":905,"nativeMethod":false,"className":"org.apache.coyote.AbstractProtocol$ConnectionHandler"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"doRun","fileName":"NioEndpoint.java","lineNumber":1746,"nativeMethod":false,"className":"org.apache.tomcat.util.net.NioEndpoint$SocketProcessor"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"run","fileName":"SocketProcessorBase.java","lineNumber":52,"nativeMethod":false,"className":"org.apache.tomcat.util.net.SocketProcessorBase"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"runWorker","fileName":"ThreadPoolExecutor.java","lineNumber":1148,"nativeMethod":false,"className":"org.apache.tomcat.util.threads.ThreadPoolExecutor"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"run","fileName":"ThreadPoolExecutor.java","lineNumber":636,"nativeMethod":false,"className":"org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"run","fileName":"TaskThread.java","lineNumber":63,"nativeMethod":false,"className":"org.apache.tomcat.util.threads.TaskThread$WrappingRunnable"},{"classLoaderName":null,"moduleName":"java.base","moduleVersion":"17.0.18","methodName":"run","fileName":"Thread.java","lineNumber":840,"nativeMethod":false,"className":"java.lang.Thread"}],"jsonRpcError":{"code":-32603,"message":"Error processing message: Stream unavailable for session d54ef607-967f-4c42-b930-3842d36eaa87"},"message":"Error processing message: Stream unavailable for session d54ef607-967f-4c42-b930-3842d36eaa87","suppressed":[],"localizedMessage":"Error processing message: Stream unavailable for session d54ef607-967f-4c42-b930-3842d36eaa87"}
    ]
    	at io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport.lambda$sendMessage$30(HttpClientStreamableHttpTransport.java:635)
    	at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:388)
    	at reactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onNext(MonoFlatMapMany.java:251)
    	at reactor.core.publisher.FluxCreate$BufferAsyncSink.drain(FluxCreate.java:880)
    	at reactor.core.publisher.FluxCreate$BufferAsyncSink.next(FluxCreate.java:805)
    	at reactor.core.publisher.FluxCreate$SerializedFluxSink.next(FluxCreate.java:163)
    	at io.modelcontextprotocol.client.transport.ResponseSubscribers$AggregateSubscriber.hookOnComplete(ResponseSubscribers.java:263)
    	at reactor.core.publisher.BaseSubscriber.onComplete(BaseSubscriber.java:197)
    	at org.reactivestreams.FlowAdapters$FlowToReactiveSubscriber.onComplete(FlowAdapters.java:221)
    	at java.net.http/jdk.internal.net.http.LineSubscriberAdapter$LineSubscription.loop(LineSubscriberAdapter.java:430)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$LockingRestartableTask.run(SequentialScheduler.java:205)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:149)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:230)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:303)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:256)
    	at java.net.http/jdk.internal.net.http.LineSubscriberAdapter$LineSubscription.signalComplete(LineSubscriberAdapter.java:193)
    	at java.net.http/jdk.internal.net.http.LineSubscriberAdapter.onComplete(LineSubscriberAdapter.java:114)
    	at java.net.http/jdk.internal.net.http.common.HttpBodySubscriberWrapper.complete(HttpBodySubscriberWrapper.java:293)
    	at java.net.http/jdk.internal.net.http.common.HttpBodySubscriberWrapper.onComplete(HttpBodySubscriberWrapper.java:401)
    	at java.net.http/jdk.internal.net.http.ResponseContent$ChunkedBodyParser.accept(ResponseContent.java:219)
    	at java.net.http/jdk.internal.net.http.ResponseContent$ChunkedBodyParser.accept(ResponseContent.java:130)
    	at java.net.http/jdk.internal.net.http.Http1Response$BodyReader.handle(Http1Response.java:691)
    	at java.net.http/jdk.internal.net.http.Http1Response$BodyReader.handle(Http1Response.java:619)
    	at java.net.http/jdk.internal.net.http.Http1Response$Receiver.accept(Http1Response.java:511)
    	at java.net.http/jdk.internal.net.http.Http1Response$BodyReader.tryAsyncReceive(Http1Response.java:649)
    	at java.net.http/jdk.internal.net.http.Http1AsyncReceiver.flush(Http1AsyncReceiver.java:233)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$LockingRestartableTask.run(SequentialScheduler.java:205)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:149)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$TryEndDeferredCompleter.complete(SequentialScheduler.java:347)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:151)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:230)
    	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    	at java.base/java.lang.Thread.run(Thread.java:840)
    11:10:55.476 [HttpClient-61-Worker-1] DEBUG io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport -- SendMessage finally: onComplete
    
    
  10. Kehrlann commented on Apr 30, 2026

    @Kehrlann
    Contributor

    Got the same error on testCompletionErrorOnMissingContext as the original: https://github.com/modelcontextprotocol/java-sdk/actions/runs/25169961716/job/73786301955?pr=943

  11. ashakirin commented on May 1, 2026

    @ashakirin
    ContributorAuthor

    Got the same error on testCompletionErrorOnMissingContext as the original: https://github.com/modelcontextprotocol/java-sdk/actions/runs/25169961716/job/73786301955?pr=943

    Error is slightly different:
    Old one: 11:10:55.445 [ForkJoinPool.commonPool-worker-2] ERROR reactor.core.publisher.Operators -- Operator called default onErrorDropped reactor.core.Exceptions$ErrorCallbackNotImplemented: java.io.IOException: HTTP/1.1 header parser received no bytes Caused by: java.io.IOException: HTTP/1.1 header parser received no bytes ... Caused by: java.io.EOFException: EOF reached while reading ... ERROR io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider -- Error handling message: Stream unavailable for session d54ef607-967f-4c42-b930-3842d36eaa87

    New one: 14:06:50.247 [boundedElastic-1] ERROR io.modelcontextprotocol.server.transport.HttpServletSseServerTransportProvider -- Failed to send message to session c2006b4a-f36f-413e-9bc0-41bea9d96f93: Client disconnected 14:06:50.248 [main] DEBUG io.modelcontextprotocol.client.LifecycleInitializer -- Joining previous initialization 14:06:50.250 [HttpClient-23-Worker-0] WARN io.modelcontextprotocol.client.transport.HttpClientSseClientTransport -- SSE stream observed an error java.io.IOException: chunked transfer encoding, state: READING_LENGTH ... Caused by: java.io.EOFException: EOF reached while reading ... "message":"Session not found: c2006b4a-f36f-413e-9bc0-41bea9d96f93","suppressed":[],"localizedMessage":"Session not found: c2006b4a-f36f-413e-9bc0-41bea9d96f93"

    But likely caused by the same reason.
    @Kehrlann: do you think it worth to investigate deeply?

  12. Kehrlann commented on May 4, 2026

    @Kehrlann
    Contributor

    It would be nice to have, but it's low priority on my own list.

  13. Kehrlann commented on May 4, 2026

    @Kehrlann
    Contributor

    Different flake in HttpClientStreamableHttpVersionNegotiationIntegrationTests.usesLatestVersion

    Error:  Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 2.068 s <<< FAILURE! -- in io.modelcontextprotocol.common.HttpClientStreamableHttpVersionNegotiationIntegrationTests
    Error:  io.modelcontextprotocol.common.HttpClientStreamableHttpVersionNegotiationIntegrationTests.usesLatestVersion -- Time elapsed: 2.041 s <<< FAILURE!
    java.lang.AssertionError: 
    
    Expected size: 3 but was: 2 in:
    [Call[method=POST, headers={content-length=54, accept=application/json, text/event-stream, host=localhost:44491, mcp-protocol-version=2025-11-25, mcp-session-id=905a6530-c071-4aad-964f-51daefc60266, user-agent=Java-http-client/17.0.18, cache-control=no-cache, content-type=application/json; charset=utf-8}, body={"jsonrpc":"2.0","method":"notifications/initialized"}],
        Call[method=POST, headers={content-length=102, accept=application/json, text/event-stream, host=localhost:44491, mcp-protocol-version=2025-11-25, mcp-session-id=905a6530-c071-4aad-964f-51daefc60266, user-agent=Java-http-client/17.0.18, cache-control=no-cache, content-type=application/json; charset=utf-8}, body={"jsonrpc":"2.0","method":"tools/call","id":"30a8a705-1","params":{"name":"test-tool","arguments":{}}}]]
    	at io.modelcontextprotocol.common.HttpClientStreamableHttpVersionNegotiationIntegrationTests.usesLatestVersion(HttpClientStreamableHttpVersionNegotiationIntegrationTests.java:79)
    
    
  14. ashakirin commented on May 22, 2026

    @ashakirin
    ContributorAuthor

    @Kehrlann
    Looked to that once more time.
    Quite sure, that the problem is broken long term SSE connection (as far as it is long, so more chances to get it broken).
    What happens:

     Failing Flow (SSE breaks under CI load)
    
      Client                                          Server
        │                                                │
        │═══ GET /sse ═══════════════════════════════════►│ doGet() L262
        │◄══ SSE event:endpoint ════════════════════════│ sessions.put("7a9438d6...", session) L302
        │                                                │
        │─── POST initialize ──────────────────────────►│ doPost() → sessions.get() → found
        │◄── HTTP 200 ──────────────────────────────────│
        │◄══ SSE: initialize response ══════════════════│ sendEvent() → SUCCESS
        │                                                │
        │─── POST completion/complete (no context) ────►│ doPost() L335
        │    {method:"completion/complete", id:"2"}      │   sessions.get("7a9438d6...") → found L374
        │                                                │   session.handle(message).block() L401
        │                                                │
        │                                                │   McpServerSession.handle() L195:
        │                                                │     L219: instanceof JSONRPCRequest → true
        │                                                │     L221: handleIncomingRequest()
        │                                                │
        │                                                │   McpServerSession.handleIncomingRequest() L253:
        │                                                │     L270: handler = requestHandlers.get("completion/complete")
        │                                                │     L278-279: handler.handle(exchange, params)
        │                                                │       → Completion handler THROWS:
        │                                                │         McpError(-32600, "Please select a database first")
        │                                                │
        │                                                │     L283: .onErrorResume(error → {
        │                                                │     L284:   error instanceof McpError → true
        │                                                │     L289:   Mono.just(JSONRPCResponse{id:"2", error:{...}})
        │                                                │     L290: })
        │                                                │
        │                                                │   Back in handle() L230:
        │                                                │     .flatMap(this.transport::sendMessage)
        │                                                │     → sends JSONRPCResponse{id:"2", error:{...}} via SSE
        │                                                │
        │                                                │   HttpServletMcpSessionTransport.sendMessage() L507:
        │                                                │     L508: Mono.fromRunnable(() → {
        │                                                │     L510:   jsonText = serialize(response)
        │                                                │     L511:   sendEvent(writer, "message", jsonText)
        │                                                │
        │         ╔══════════════════════════════════════╗│   sendEvent() L454:
        │         ║ SSE CONNECTION BROKEN (CI LOAD)     ║│     L455: writer.write("event: message\n")
        │         ║ TCP EOF on chunked transfer         ║│     L456: writer.write("data: ...\n\n")
        │         ╚══════════════════════════════════════╝│     L457: writer.flush()
        │                                                │     L459: writer.checkError() → TRUE ← !!!!
        │                                                │     L460: throw new IOException("Client disconnected")
        │                                                │
        │                                                │   sendMessage() catch block L514:
        │                                                │     L515: logger.error("Failed to send message to
        │                                                │            session 7a9438d6...: Client disconnected")
        │                                                │     L516: sessions.remove("7a9438d6...")  ← SESSION GONE
        │                                                │     L517: asyncContext.complete()
        │                                                │     L518: }  ← Runnable finishes, Mono completes normally
        │                                                │
        │                                                │   handle().block() returns (Mono<Void> completed) L401
        │◄── HTTP 200 ──────────────────────────────────│   response.setStatus(200) L403
        │                                                │
        │                                                │
        │  CLIENT SIDE:                                  │
        │                                                │
        │  HttpClientSseClientTransport L373:            │
        │    .onErrorComplete(t → {                      │
        │      L375: logger.warn("SSE stream observed    │
        │             an error", t)                      │
        │      // t = IOException: chunked transfer      │
        │      //     encoding, state: READING_LENGTH    │
        │      //   Caused by: EOFException: EOF reached │
        │    })                                          │
        │                                                │
        │  McpClientSession.sendRequest() L251:          │
        │    pendingResponses("2") NEVER receives value  │
        │    L261: .timeout(requestTimeout) → eventually │
        │    throws TimeoutException (or similar)        │
        │                                                │
        │  *** FAILURE MODE A: ***                       │
        │  assertThatExceptionOfType(McpError.class)     │
        │    → gets TimeoutException/RuntimeException    │
        │    → assertion FAILS (wrong exception type)    │
        │                                                │
        │         ─── OR ───                             │
        │                                                │
        │  If timing allows first response to squeak     │
        │  through before SSE fully breaks:              │
        │  *** FAILURE MODE B: ***                       │
        │  First assertion passes, then:                 │
        │                                                │
        │─── POST completion/complete (with context) ──►│ doPost() L335
        │    {method:"completion/complete", id:"3",      │
        │     context:{database:"test_db"}}              │
        │                                                │   L359: sessionId = "7a9438d6..."
        │                                                │   L374: session = sessions.get("7a9438d6...")
        │                                                │         → NULL ← !!!
        │                                                │   L375: if (session == null) {
        │                                                │   L378:   response.setStatus(404)
        │                                                │   L379-380: "Session not found: 7a9438d6..."
        │◄── HTTP 404 ──────────────────────────────────│   L384:   writer.flush()
        │    body: "Session not found: 7a9438d6..."      │
        │                                                │
        │  HttpClientSseClientTransport.sendMessage()    │
        │  L413: response.statusCode() == 404            │
        │  L415: sink.error(new RuntimeException(        │
        │    "Sending message failed with a non-OK HTTP  │
        │     code: 404 - Session not found: 7a9438d6")) │
        │  L425: logger.error("Error sending message:    │
        │    Sending message failed with non-OK HTTP     │
        │    code: 404")                                 │
        │                                                │
        │  McpClientSession.sendRequest() L256:          │
        │    transport.sendMessage() subscription:       │
        │    L258: pendingResponses.remove("3")          │
        │    L259: pendingResponseSink.error(            │
        │      RuntimeException("...404..."))            │
        │                                                │
        │  McpSyncClient.completeCompletion() L442:      │
        │    .block() → throws RuntimeException          │
        │                                                │
        │  *** TEST FAILS ***                            │
        │  RuntimeException: "Sending message failed     │
        │    with a non-OK HTTP code: 404 -              │
        │    Session not found: 7a9438d6..."             │
        │                                                │
    
    

    I see three possible ways to act:

    1. don't change anything, failures are not often. Keep in mind, that tests using SSE can be flaky
    2. apply retry mechanism for SSE tests
      a) explicitly in code (like in my initial PR)
      b) using surefire's rerunFailingTestsCount
      c) custom JUnit extension
    3. add reconnect mechanism for broken SSE connection (and adapt tests accordingly) - new feature
      a) Transport-level reconnect
      b) Client-level reconnect:
      public CompleteResult completeCompletion(CompleteRequest request) {
          try {
              return delegate.completeCompletion(request).block();
          } catch (McpTransportException e) {
              if (e.isConnectionLost()) {
                  reconnectAndReinitialize();
                  return delegate.completeCompletion(request).block();  // retry on new session
              }
              throw e;
          }
      }
    

    WDYT?

  15. Kehrlann commented on Jun 16, 2026

    @Kehrlann
    Contributor

    We have much less errors since #995 was merged, which fixed some client-side error handling. Let's see if we have more flakes going forward.

  16. Kehrlann commented on Jun 23, 2026

    @Kehrlann
    Contributor

    New flake for HttpServletStreamableIntegrationTests.testRootsSuccess

    Source: https://github.com/modelcontextprotocol/java-sdk/actions/runs/28031127085/job/82971782971
    Edit: another instance - https://github.com/modelcontextprotocol/java-sdk/actions/runs/28098407134/job/83193642245

    Stack trace:

    Error:  io.modelcontextprotocol.server.HttpServletStreamableIntegrationTests.testRootsSuccess -- Time elapsed: 0.035 s <<< ERROR!
    java.lang.RuntimeException: 
    Failed to send message: AggregateResponseEvent[responseInfo=jdk.internal.net.http.ResponseInfoImpl@388fab35, data={"cause":null,"stackTrace":[{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"build","fileName":"McpError.java","lineNumber":81,"nativeMethod":false,"className":"io.modelcontextprotocol.spec.McpError$Builder"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"doPost","fileName":"HttpServletStreamableServerTransportProvider.java","lineNumber":551,"nativeMethod":false,"className":"io.modelcontextprotocol.server.transport.HttpServletStreamableServerTransportProvider"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"service","fileName":"HttpServlet.java","lineNumber":645,"nativeMethod":false,"className":"jakarta.servlet.http.HttpServlet"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"service","fileName":"HttpServlet.java","lineNumber":716,"nativeMethod":false,"className":"jakarta.servlet.http.HttpServlet"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"doFilter","fileName":"ApplicationFilterChain.java","lineNumber":130,"nativeMethod":false,"className":"org.apache.catalina.core.ApplicationFilterChain"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"StandardWrapperValve.java","lineNumber":167,"nativeMethod":false,"className":"org.apache.catalina.core.StandardWrapperValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"StandardContextValve.java","lineNumber":79,"nativeMethod":false,"className":"org.apache.catalina.core.StandardContextValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"AuthenticatorBase.java","lineNumber":483,"nativeMethod":false,"className":"org.apache.catalina.authenticator.AuthenticatorBase"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"StandardHostValve.java","lineNumber":115,"nativeMethod":false,"className":"org.apache.catalina.core.StandardHostValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"ErrorReportValve.java","lineNumber":93,"nativeMethod":false,"className":"org.apache.catalina.valves.ErrorReportValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"invoke","fileName":"StandardEngineValve.java","lineNumber":74,"nativeMethod":false,"className":"org.apache.catalina.core.StandardEngineValve"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"service","fileName":"CoyoteAdapter.java","lineNumber":342,"nativeMethod":false,"className":"org.apache.catalina.connector.CoyoteAdapter"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"service","fileName":"Http11Processor.java","lineNumber":395,"nativeMethod":false,"className":"org.apache.coyote.http11.Http11Processor"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"process","fileName":"AbstractProcessorLight.java","lineNumber":63,"nativeMethod":false,"className":"org.apache.coyote.AbstractProcessorLight"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"process","fileName":"AbstractProtocol.java","lineNumber":905,"nativeMethod":false,"className":"org.apache.coyote.AbstractProtocol$ConnectionHandler"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"doRun","fileName":"NioEndpoint.java","lineNumber":1746,"nativeMethod":false,"className":"org.apache.tomcat.util.net.NioEndpoint$SocketProcessor"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"run","fileName":"SocketProcessorBase.java","lineNumber":52,"nativeMethod":false,"className":"org.apache.tomcat.util.net.SocketProcessorBase"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"runWorker","fileName":"ThreadPoolExecutor.java","lineNumber":1148,"nativeMethod":false,"className":"org.apache.tomcat.util.threads.ThreadPoolExecutor"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"run","fileName":"ThreadPoolExecutor.java","lineNumber":636,"nativeMethod":false,"className":"org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker"},{"classLoaderName":"app","moduleName":null,"moduleVersion":null,"methodName":"run","fileName":"TaskThread.java","lineNumber":63,"nativeMethod":false,"className":"org.apache.tomcat.util.threads.TaskThread$WrappingRunnable"},{"classLoaderName":null,"moduleName":"java.base","moduleVersion":"17.0.19","methodName":"run","fileName":"Thread.java","lineNumber":840,"nativeMethod":false,"className":"java.lang.Thread"}],"jsonRpcError":{"code":-32603,"message":"Error processing message: Stream unavailable for session 05bbff87-4975-4f26-bd81-b56355a014b4"},"message":"Error processing message: Stream unavailable for session 05bbff87-4975-4f26-bd81-b56355a014b4","suppressed":[],"localizedMessage":"Error processing message: Stream unavailable for session 05bbff87-4975-4f26-bd81-b56355a014b4"}
    ]
    	at io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport.lambda$sendMessage$30(HttpClientStreamableHttpTransport.java:662)
    	at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:388)
    	at reactor.core.publisher.FluxCreate$BufferAsyncSink.drain(FluxCreate.java:880)
    	at reactor.core.publisher.FluxCreate$BufferAsyncSink.next(FluxCreate.java:805)
    	at reactor.core.publisher.FluxCreate$SerializedFluxSink.next(FluxCreate.java:163)
    	at io.modelcontextprotocol.client.transport.ResponseSubscribers$AggregateSubscriber.hookOnComplete(ResponseSubscribers.java:263)
    	at reactor.core.publisher.BaseSubscriber.onComplete(BaseSubscriber.java:197)
    	at org.reactivestreams.FlowAdapters$FlowToReactiveSubscriber.onComplete(FlowAdapters.java:221)
    	at java.net.http/jdk.internal.net.http.LineSubscriberAdapter$LineSubscription.loop(LineSubscriberAdapter.java:430)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$LockingRestartableTask.run(SequentialScheduler.java:205)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:149)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:230)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:303)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:256)
    	at java.net.http/jdk.internal.net.http.LineSubscriberAdapter$LineSubscription.signalComplete(LineSubscriberAdapter.java:193)
    	at java.net.http/jdk.internal.net.http.LineSubscriberAdapter.onComplete(LineSubscriberAdapter.java:114)
    	at java.net.http/jdk.internal.net.http.common.HttpBodySubscriberWrapper.complete(HttpBodySubscriberWrapper.java:293)
    	at java.net.http/jdk.internal.net.http.common.HttpBodySubscriberWrapper.onComplete(HttpBodySubscriberWrapper.java:401)
    	at java.net.http/jdk.internal.net.http.ResponseContent$ChunkedBodyParser.accept(ResponseContent.java:219)
    	at java.net.http/jdk.internal.net.http.ResponseContent$ChunkedBodyParser.accept(ResponseContent.java:130)
    	at java.net.http/jdk.internal.net.http.Http1Response$BodyReader.handle(Http1Response.java:691)
    	at java.net.http/jdk.internal.net.http.Http1Response$BodyReader.handle(Http1Response.java:619)
    	at java.net.http/jdk.internal.net.http.Http1Response$Receiver.accept(Http1Response.java:511)
    	at java.net.http/jdk.internal.net.http.Http1Response$BodyReader.tryAsyncReceive(Http1Response.java:649)
    	at java.net.http/jdk.internal.net.http.Http1AsyncReceiver.flush(Http1AsyncReceiver.java:233)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$LockingRestartableTask.run(SequentialScheduler.java:205)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:149)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$TryEndDeferredCompleter.complete(SequentialScheduler.java:347)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:151)
    	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:230)
    	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    	at java.base/java.lang.Thread.run(Thread.java:840)
    	Suppressed: java.lang.Exception: #block terminated with an error
    		at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:104)
    		at reactor.core.publisher.Mono.block(Mono.java:1779)
    		at io.modelcontextprotocol.client.McpSyncClient.rootsListChangedNotification(McpSyncClient.java:197)
    		at io.modelcontextprotocol.AbstractMcpClientServerIntegrationTests.testRootsSuccess(AbstractMcpClientServerIntegrationTests.java:1010)
    		at java.base/java.lang.reflect.Method.invoke(Method.java:569)
    		at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    		at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    
  17. Kehrlann commented on Jun 24, 2026

    @Kehrlann
    Contributor

    Flake on the usual McpCompletionTests.testCompletionErrorOnMissingContext:

    https://github.com/modelcontextprotocol/java-sdk/actions/runs/28091914820/job/83171754206

    But this time it's a timeout:

    java.lang.AssertionError: 
    
    Expecting actual throwable to be an instance of:
      io.modelcontextprotocol.spec.McpError
    but was:
      reactor.core.Exceptions$ReactiveException: java.util.concurrent.TimeoutException: Did not observe any item or terminal signal within 20000ms in 'source(MonoDeferContextual)' (and no fallback has been configured)
    	at reactor.core.Exceptions.propagate(Exceptions.java:410)
    	at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:102)
    	at reactor.core.publisher.Mono.block(Mono.java:1779)
    	...(85 remaining lines not displayed - this can be changed with Assertions.setMaxStackTraceElementsDisplayed)
    	at io.modelcontextprotocol.server.McpCompletionTests.testCompletionErrorOnMissingContext(McpCompletionTests.java:318)
    
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Nice to haves, rare edge casesneeds reproWe need a way to reproduce the problemtestsAddresses tests only

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions