Skip to content

Client-caused AbortedException gets logged as error #36811

@mv-shallow

Description

@mv-shallow

Version: org.springframework.boot:spring-boot-starter-webflux:3.5.8 / org.springframework:spring-web:6.2.14

I have a service that uses Reactor Netty.

When client closes connection before sending full request body there is a log with ERROR level:

2026-05-18T14:22:17.887+03:00 ERROR 9144 --- [tor-http-nio-11] o.s.w.s.adapter.HttpWebHandlerAdapter    : [e8392712-7] 500 Server Error for HTTP POST "/test"

reactor.netty.channel.AbortedException: Connection has been closed
	at reactor.netty.http.server.HttpServerOperations.onInboundClose(HttpServerOperations.java:931) ~[reactor-netty-http-1.2.12.jar:1.2.12]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	*__checkpoint ? HTTP POST "/test" [ExceptionHandlingWebHandler]
Original Stack Trace:
		at reactor.netty.http.server.HttpServerOperations.onInboundClose(HttpServerOperations.java:931) ~[reactor-netty-http-1.2.12.jar:1.2.12]

Which is a bit unexpected since it's a client-side issue related to connection, so there is seemingly no need for it to be logged as ERROR by default and have any status code. (Also undesirable because in my case there are alerts configured for any logs with ERROR level)

I'd expect it to be logged at WARN/DEBUG/TRACE and this line does exactly that, but checkAndLogClientDisconnectedException isn't executed since response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR) returns true.

This looks like a bug since the code for "expected" behavior is there but is unreachable. If it's not a bug or a limitation of connectivity-related error handling, how can I safely provide my own handling for such case, via ExceptionHandler?

Below are some snippets for reproducing this issue

Main.java:

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

    @Bean
    RouterFunction<?> routerFunction() {
        return RouterFunctions
            .route()
            .POST(
                "/test",
                request -> request
                    .bodyToMono(String.class)
                    .flatMap(body -> ServerResponse.ok().build())
            ).build();
    }
}

build.gradle:

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.5.8'
    id 'io.spring.dependency-management' version '1.1.7'
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
}

python script for producing aborted request:

import socket

HOST = "127.0.0.1"
PORT = 8080

request = (
    b"POST /test HTTP/1.1\r\n"
    b"Host: localhost\r\n"
    b"Content-Type: text/plain\r\n"
    b"Content-Length: 2\r\n"
    b"\r\n"
)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
sock.sendall(request)
sock.sendall(b"A")
sock.close()

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions