Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IOException occurs in Flux Emission When Client Disconnects in Spring Boot WebFlux (Tomcat Server) #34005

Open
patrickf314 opened this issue Dec 2, 2024 · 5 comments
Labels
status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged or decided on

Comments

@patrickf314
Copy link

Description

When using Spring Boot with WebFlux and returning a Flux from a REST controller, an IOException is thrown when the client disconnects. The issue occurs specifically when using the Tomcat server. The same code works as expected when using the Netty server: Flux is canceled and not exception is passed to the exception handler. The error still occurs with spring boot 3.4.0.

This behavior is problematic because:

  1. The Spring Boot exception handler catches the IOException (e.g., "Broken pipe" or "Connection reset by peer"), even though this should be handled gracefully.
  2. There is no way to detect if the socket is closed before writing to it.
  3. The exception clutters the logs and disrupts application workflows, even when the disconnection is a normal client behavior.

I believe this is a bug introduced in Spring Boot version 3.2.6 or an inconsistency in the handling of disconnections between Tomcat and Netty servers.

Steps to Reproduce

  1. Create a Spring Boot WebFlux application with a REST controller that streams data as a Flux.
  2. Configure the application to use the Tomcat server.
  3. Start the application and connect to the endpoint using a client (e.g., curl or Postman).
  4. Disconnect the client before the stream completes.
  5. Observe that an IOException is thrown and handled by Spring Boot’s exception handler.

Sample Application

<!-- pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>de.spring.bug</groupId>
    <artifactId>spring-flux-bug</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <!-- Since 3.2.6 -->
        <spring-boot.version>3.2.6</spring-boot.version>
    </properties>

    <dependencies>
        <!-- Spring Web to have spring boot started with Tomcat -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>
    </dependencies>
</project>
package de.spring.flux.bug;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;

@SpringBootApplication
@RestController
public class SpringFluxBugApplication {

    private static final Logger LOG = LoggerFactory.getLogger(SpringFluxBugApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(SpringFluxBugApplication.class, args);
    }

    @ExceptionHandler(Throwable.class)
    public void handleException(Throwable throwable) {
        // No invoked when using Netty.
        LOG.error("Unhandled exception", throwable);
    }

    @GetMapping("bugs")
    public Flux<String> getBugs() {
        return Flux.interval(Duration.ofSeconds(1))
                .map(i -> "Spring flux bug " + i)
                .log();
    }

    @Bean
    public WebExceptionHandler exceptionHandler() {
        return (exchange, ex) -> {
            LOG.info("This exception handler is not called", ex);
            return Mono.empty();
        };
    }
}

Start the spring boot application and call curl -N http://localhost:8080/bugs, then you should get the first string responses and then you can cancel the curl command. This will cause the following log in the application:

2024-12-02T16:07:32.477+01:00  INFO 82220 --- [        task-10] reactor.Flux.Map.2                       : request(1)
2024-12-02T16:07:33.476+01:00  INFO 82220 --- [     parallel-2] reactor.Flux.Map.2                       : onNext(Spring flux bug 5)
2024-12-02T16:07:33.477+01:00  INFO 82220 --- [        task-11] reactor.Flux.Map.2                       : cancel()
2024-12-02T16:07:33.477+01:00 ERROR 82220 --- [nio-8080-exec-5] d.s.flux.bug.SpringFluxBugApplication    : Unhandled exception

java.io.IOException: Broken pipe
	at java.base/sun.nio.ch.SocketDispatcher.write0(Native Method) ~[na:na]
	at java.base/sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:62) ~[na:na]
	at java.base/sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:137) ~[na:na]
	at java.base/sun.nio.ch.IOUtil.write(IOUtil.java:102) ~[na:na]
	at java.base/sun.nio.ch.IOUtil.write(IOUtil.java:58) ~[na:na]
	at java.base/sun.nio.ch.SocketChannelImpl.implWrite(SocketChannelImpl.java:566) ~[na:na]
	at java.base/sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:618) ~[na:na]
	at org.apache.tomcat.util.net.NioChannel.write(NioChannel.java:122) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
	at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.doWrite(NioEndpoint.java:1378) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
	at org.apache.tomcat.util.net.SocketWrapperBase.doWrite(SocketWrapperBase.java:764) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
	at org.apache.tomcat.util.net.SocketWrapperBase.flushBlocking(SocketWrapperBase.java:728) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
	at org.apache.tomcat.util.net.SocketWrapperBase.flush(SocketWrapperBase.java:712) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
	at org.apache.coyote.http11.Http11OutputBuffer$SocketOutputBuffer.flush(Http11OutputBuffer.java:559) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
	at org.apache.coyote.http11.filters.ChunkedOutputFilter.flush(ChunkedOutputFilter.java:157) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
	at org.apache.coyote.http11.Http11OutputBuffer.flush(Http11OutputBuffer.java:216) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
	at org.apache.coyote.http11.Http11Processor.flush(Http11Processor.java:1244) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
	at org.apache.coyote.AbstractProcessor.action(AbstractProcessor.java:400) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
	at org.apache.coyote.Response.action(Response.java:208) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
	at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:299) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
	at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:265) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
	at org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:136) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
	at org.springframework.web.context.request.async.StandardServletAsyncWebRequest$LifecycleServletOutputStream.flush(StandardServletAsyncWebRequest.java:389) ~[spring-web-6.1.8.jar:6.1.8]
	at org.springframework.util.StreamUtils.copy(StreamUtils.java:135) ~[spring-core-6.1.8.jar:6.1.8]
	at org.springframework.http.converter.StringHttpMessageConverter.writeInternal(StringHttpMessageConverter.java:128) ~[spring-web-6.1.8.jar:6.1.8]
	at org.springframework.http.converter.StringHttpMessageConverter.writeInternal(StringHttpMessageConverter.java:44) ~[spring-web-6.1.8.jar:6.1.8]
	at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:235) ~[spring-web-6.1.8.jar:6.1.8]
	at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler$HttpMessageConvertingHandler.sendInternal(ResponseBodyEmitterReturnValueHandler.java:221) ~[spring-webmvc-6.1.8.jar:6.1.8]
	at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler$HttpMessageConvertingHandler.send(ResponseBodyEmitterReturnValueHandler.java:205) ~[spring-webmvc-6.1.8.jar:6.1.8]
	at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter.send(ResponseBodyEmitter.java:188) ~[spring-webmvc-6.1.8.jar:6.1.8]
	at org.springframework.web.servlet.mvc.method.annotation.ReactiveTypeHandler$TextEmitterSubscriber.send(ReactiveTypeHandler.java:445) ~[spring-webmvc-6.1.8.jar:6.1.8]
	at org.springframework.web.servlet.mvc.method.annotation.ReactiveTypeHandler$AbstractEmitterSubscriber.run(ReactiveTypeHandler.java:332) ~[spring-webmvc-6.1.8.jar:6.1.8]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
	at java.base/java.lang.Thread.run(Thread.java:1575) ~[na:na]

Expected Behavior

  • It should be possible to explicitly handle this exception.
  • Spring boot should behave similarly for Netty and Tomcat.

Details

In our application, the exception handler logs unexpected exception as IOException as ERROR. Having the client disconnect exception handled this way spams the logs with unwanted exceptions. We can use the Flux#doOnCancel method to handle the disconnection in of the Flux. However, there no way of preventing the IOException to be passed to the exception handler.
Also spring boot should behave similarly for Netty and Tomcat. I think, it is not a good practice to filter the exception of the exception handle to specifically look for an IOException with message Broken pipe. Possible solutions might be to suppress this IOException or alternatively wrap it with e.g. a org.springframework.web.context.request.async.AsyncRequestNotUsableException, which then can be handled explicitly.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Dec 2, 2024
@wilkinsona
Copy link
Member

Thanks for the report.

When using Spring Boot with WebFlux

You aren't using WebFlux here. When you declare a dependency on both spring-boot-starter-web and spring-boot-starter-webflux, the former wins and your application will used the Servlet-based web stack.

spring boot should behave similarly for Netty and Tomcat

A more like-for-like comparison of Tomcat and Netty would be to depend on only spring-boot-starter-webflux, and then exclude spring-boot-starter-reactor-netty in favor of a dependency on spring-boot-starter-tomcat. That will result in a reactive app running on top of Tomcat through org.springframework.http.server.reactive.TomcatHttpHandlerAdapter.

Please give that a try and let us know how it goes.

@wilkinsona wilkinsona added the status: waiting-for-feedback We need additional information before we can continue label Dec 2, 2024
@patrickf314
Copy link
Author

Ok, thanks for the quick response. So, just for clarification, in our application we mainly want to use servlet-based web stack, but a few endpoints are returning server send events responses (using Flux, same problem occurs with SSEEmitters). This is due to the fact, that the responses of these endpoint are async and depend on server event, like status updates.
In principle, we don't mind about the difference between Netty and Tomcat (This is rather an observation while testing). Also we strongly want to stick with Tomcat.

A more like-for-like comparison of Tomcat and Netty would be to depend on only spring-boot-starter-webflux, and then exclude spring-boot-starter-reactor-netty in favor of a dependency on spring-boot-starter-tomcat. That will result in a reactive app running on top of Tomcat through org.springframework.http.server.reactive.TomcatHttpHandlerAdapter.

Anyway, I tested your suggestions. Still I get a similar exception in the exception handler but with a different stacktrace:

2024-12-02T17:05:35.167+01:00 ERROR 91724 --- [nio-8080-exec-3] d.s.flux.bug.SpringFluxBugApplication    : Unhandled exception

java.io.IOException: Broken pipe
	at java.base/sun.nio.ch.SocketDispatcher.write0(Native Method) ~[na:na]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Assembly trace from producer [reactor.core.publisher.MonoFromPublisher] :
	reactor.core.publisher.Mono.from(Mono.java:508)
	org.springframework.http.server.reactive.AbstractListenerServerHttpResponse.writeAndFlushWithInternal(AbstractListenerServerHttpResponse.java:63)
Error has been observed at the following site(s):
	*________Mono.from ⇢ at org.springframework.http.server.reactive.AbstractListenerServerHttpResponse.writeAndFlushWithInternal(AbstractListenerServerHttpResponse.java:63)
	*__Flux.concatWith ⇢ at org.springframework.http.server.reactive.AbstractServerHttpResponse.doCommit(AbstractServerHttpResponse.java:266)
	|_       Flux.then ⇢ at org.springframework.http.server.reactive.AbstractServerHttpResponse.doCommit(AbstractServerHttpResponse.java:269)
	*___Mono.doOnError ⇢ at org.springframework.http.server.reactive.AbstractServerHttpResponse.writeAndFlushWith(AbstractServerHttpResponse.java:217)
	|_      checkpoint ⇢ Handler de.spring.flux.bug.SpringFluxBugApplication#getBugs() [DispatcherHandler]
Original Stack Trace:
		at java.base/sun.nio.ch.SocketDispatcher.write0(Native Method) ~[na:na]
		at java.base/sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:62) ~[na:na]
		at java.base/sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:137) ~[na:na]
		at java.base/sun.nio.ch.IOUtil.write(IOUtil.java:102) ~[na:na]
		at java.base/sun.nio.ch.IOUtil.write(IOUtil.java:58) ~[na:na]
		at java.base/sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:542) ~[na:na]
		at org.apache.tomcat.util.net.NioChannel.write(NioChannel.java:122) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
		at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.doWrite(NioEndpoint.java:1408) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
		at org.apache.tomcat.util.net.SocketWrapperBase.doWrite(SocketWrapperBase.java:764) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
		at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.flushNonBlocking(NioEndpoint.java:1306) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
		at org.apache.tomcat.util.net.SocketWrapperBase.flush(SocketWrapperBase.java:714) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
		at org.apache.coyote.http11.Http11OutputBuffer$SocketOutputBuffer.flush(Http11OutputBuffer.java:559) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
		at org.apache.coyote.http11.filters.ChunkedOutputFilter.flush(ChunkedOutputFilter.java:157) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
		at org.apache.coyote.http11.Http11OutputBuffer.flush(Http11OutputBuffer.java:216) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
		at org.apache.coyote.http11.Http11Processor.flush(Http11Processor.java:1244) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
		at org.apache.coyote.AbstractProcessor.action(AbstractProcessor.java:400) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
		at org.apache.coyote.Response.action(Response.java:208) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
		at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:299) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
		at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:265) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
		at org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:136) ~[tomcat-embed-core-10.1.24.jar:10.1.24]
		at org.springframework.http.server.reactive.ServletServerHttpResponse.flush(ServletServerHttpResponse.java:236) ~[spring-web-6.1.8.jar:6.1.8]
		at org.springframework.http.server.reactive.ServletServerHttpResponse$ResponseBodyFlushProcessor.flush(ServletServerHttpResponse.java:347) ~[spring-web-6.1.8.jar:6.1.8]
		at org.springframework.http.server.reactive.AbstractListenerWriteFlushProcessor$State$3.writeComplete(AbstractListenerWriteFlushProcessor.java:312) ~[spring-web-6.1.8.jar:6.1.8]
		at org.springframework.http.server.reactive.AbstractListenerWriteFlushProcessor$State$WriteResultSubscriber.onComplete(AbstractListenerWriteFlushProcessor.java:465) ~[spring-web-6.1.8.jar:6.1.8]
		at org.springframework.http.server.reactive.WriteResultPublisher$State.publishComplete(WriteResultPublisher.java:266) ~[spring-web-6.1.8.jar:6.1.8]
		at org.springframework.http.server.reactive.WriteResultPublisher$State$1.subscribe(WriteResultPublisher.java:171) ~[spring-web-6.1.8.jar:6.1.8]
		at org.springframework.http.server.reactive.WriteResultPublisher.subscribe(WriteResultPublisher.java:77) ~[spring-web-6.1.8.jar:6.1.8]
		at org.springframework.http.server.reactive.AbstractListenerWriteProcessor.subscribe(AbstractListenerWriteProcessor.java:205) ~[spring-web-6.1.8.jar:6.1.8]
		at org.springframework.http.server.reactive.AbstractListenerWriteFlushProcessor$State$2.onNext(AbstractListenerWriteFlushProcessor.java:294) ~[spring-web-6.1.8.jar:6.1.8]
		at org.springframework.http.server.reactive.AbstractListenerWriteFlushProcessor.onNext(AbstractListenerWriteFlushProcessor.java:120) ~[spring-web-6.1.8.jar:6.1.8]
		at org.springframework.http.server.reactive.AbstractListenerWriteFlushProcessor.onNext(AbstractListenerWriteFlushProcessor.java:43) ~[spring-web-6.1.8.jar:6.1.8]
		at org.springframework.http.server.reactive.ChannelSendOperator$WriteBarrier.onNext(ChannelSendOperator.java:173) ~[spring-web-6.1.8.jar:6.1.8]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:210) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxInterval$IntervalRunnable.run(FluxInterval.java:124) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.scheduler.PeriodicWorkerTask.call(PeriodicWorkerTask.java:59) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.scheduler.PeriodicWorkerTask.run(PeriodicWorkerTask.java:73) ~[reactor-core-3.6.6.jar:3.6.6]
		at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) ~[na:na]
		at java.base/java.util.concurrent.FutureTask.runAndReset$$$capture(FutureTask.java:358) ~[na:na]
		at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java) ~[na:na]
		at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[na:na]
		at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
		at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
		at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 2, 2024
@wilkinsona
Copy link
Member

Thanks for trying the reactive stack running on Tomcat and confirming that you see a different yet still unwanted exception.

The code that's involved with the IO is part of Spring Framework and its behavior is largely out of Boot's control. We'll transfer this issue to the Framework team so that they can investigate.

@snicoll snicoll transferred this issue from spring-projects/spring-boot Dec 2, 2024
@bclozel
Copy link
Member

bclozel commented Dec 2, 2024

This is unfortunately the expected behavior. For all servers relying on the Servlet API, we do not have an API that notifies when a client disconnects. The only way to find out is to keep writing.

See jakartaee/servlet#44

Now we do suppress a few of these cases in logs. If I understand correctly, you are seeing more of those after a recent upgrade? Which content type is this using? Is this using server sent events?

@bclozel bclozel added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Dec 2, 2024
@patrickf314
Copy link
Author

This is unfortunately the expected behavior

Ok, I see. As I suggested, it would be nice to register a distinguished exception handler for those kind of exceptions. However, for those who find this ticket. Here is the way we handled it now: There is a org.springframework.web.util.DisconnectedClientHelper which helps identifying those exceptions. So basically we changed to exception handler to

@ExceptionHandler(Throwable.class)
public void handleException(Throwable throwable) {
    if (DisconnectedClientHelper.isClientDisconnectedException(e)) {
        LOG.debug("Client disconnected.", e);
    }else{
        LOG.error("Unhandled exception", throwable);
    }
}

If I understand correctly, you are seeing more of those after a recent upgrade?

Yes, thats correct. Since the update from spring boot 3.2.5 to 3.2.6, we see those exception regularly in our CI. This also can be reproduced using the example above and changing the spring boot version to 3.2.5. However, using only the spring-boot-starter-webflux (with tomcat) does also log the exception. For both dependencies spring-boot-starter-web and spring-boot-starter-webflux, the IOException is not passed to the exception handler and hence not logged as ERROR.

This might be a problem in a transitive dependencies. At first glance, I didn't see any version problem: spring-core, spring-web, spring-webmvc and spring-webflux are only included with version 6.1.6. Maybe you can give me a hint where to look. I also don't know which part of spring does the error handling in this case. As @wilkinsona pointed out, the spring-webmvc part takes over.

Which content type is this using? Is this using server sent events?

Some endpoints use produces = MediaType.TEXT_EVENT_STREAM_VALUE other don't specify anything (In both cases, the exception can be observed). I guess, this is the default value as without specifying, the server returns content-type: text/event-stream.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged or decided on
Projects
None yet
Development

No branches or pull requests

4 participants