When a controller returns Flux<ServerSentEvent>
, SseEmitter
is used to write events. If the connection drops, the write fails with IOException in a Spring MVC TaskExecutor thread while in parallel we also get a Servlet container AsyncListener#onError
callback.
In #32340, WebAsyncManager
was enhanced to be protected in this race, so only one thread can dispatch. However, it is also crucial to ensure the onError
callback holds up until the dispatch completes regardless of who wins. WebAsyncManager#setConcurrentResultAndDispatch
guarantees this through synchronization, but when using DeferredResult
, the onError
callback may not call that method at all if DeferredResult error handling finds the result is already set by the competing thread. As a result, the onError callback may exit the container thread before dispatch in the Spring MVC thread compltes, which leads to the stacktrace in https://github.com/spring-projects/spring-framework/issues/33421#issuecomment-2352904761 and also in #34188.
The issue does not apply when using Callable
, e.g. via StreamingResponseBody
as in that case setConcurrentResultAndDispatch
is always called.