Skip to content

UnsupportedOperationException in NettyRoutingFilter when handling immutable ReadOnlyHttpHeaders #4270

Description

@maxx-ukoo

Describe the bug
When routing requests through Spring Cloud Gateway, the NettyRoutingFilter attempts to remove headers from a ReadOnlyHttpHeaders instance. This results in a java.lang.UnsupportedOperationException.

Although the response is still committed successfully (200 OK), the error is logged, which is misleading and suggests unexpected behavior in the filter chain.

Spring Boot version: 4.0.6/4.1.1
Spring Cloud Gateway: 2025.1.1/20.25.1.2

Sample
No sample application but i can try to create if need.
Logs:

2026-08-26T08:37:40.345Z TRACE 1 --- [gateway] [or-http-epoll-3] o.s.w.s.adapter.HttpWebHandlerAdapter    : [37da73a0-12] HTTP GET "/AAA/BBB", headers={masked}
2026-08-26T08:37:40.421Z  INFO 1 --- [gateway] [or-http-epoll-3] c.c.a.hub.gateway.config.LoggingFilter   : Incoming request https://example.com/AAA/BBB is routed to id: BBB-ui, uri: http://BBB-ui:80/AAA/BBB
2026-08-26T08:37:40.445Z ERROR 1 --- [gateway] [or-http-epoll-3] c.c.a.h.g.h.TopLevelExceptionHandler     : Caught exception

java.lang.UnsupportedOperationException
        at org.springframework.http.ReadOnlyHttpHeaders.remove(ReadOnlyHttpHeaders.java:166) ~[spring-web-7.0.9.jar!/:7.0.9]
        Suppressed: The stacktrace has been enhanced by Reactor, refer to additional information below: 
Error has been observed at the following site(s):
        *__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter@30704f85
        *__checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter@43115b17
        *__checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter@79976e07
        *__checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter@4f229e87
        *__checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter@2e66a2b
        *__checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter@2475bbb3
        *__checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter@12284f0d
        *__checkpoint ⇢ org.springframework.security.oauth2.client.web.server.authentication.OAuth2LoginAuthenticationWebFilter@21cf894
        *__checkpoint ⇢ org.springframework.security.oauth2.client.web.server.OAuth2AuthorizationRequestRedirectWebFilter@28fbda1c
        *__checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter@682d6318
        *__checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter@5515313b
        *__checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter@2c02c49e
        *__checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy@32328dc4
        *__checkpoint ⇢ HTTP GET "/AAA/BBB" [ExceptionHandlingWebHandler]
Original Stack Trace:
                at org.springframework.http.ReadOnlyHttpHeaders.remove(ReadOnlyHttpHeaders.java:166) ~[spring-web-7.0.9.jar!/:7.0.9]
                at org.springframework.cloud.gateway.filter.NettyRoutingFilter.lambda$filter$4(NettyRoutingFilter.java:182) ~[spring-cloud-gateway-server-webflux-5.0.2.jar!/:5.0.2]
                at reactor.netty.http.client.HttpClientFinalizer.lambda$responseConnection$4(HttpClientFinalizer.java:102) ~[reactor-netty-http-1.3.7.jar!/:1.3.7]
                at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:164) ~[reactor-core-3.8.7.jar!/:3.8.7]
                at reactor.core.publisher.SerializedSubscriber.onNext(SerializedSubscriber.java:100) ~[reactor-core-3.8.7.jar!/:3.8.7]
                at reactor.core.publisher.FluxRetryWhen$RetryWhenMainSubscriber.onNext(FluxRetryWhen.java:179) ~[reactor-core-3.8.7.jar!/:3.8.7]
                at reactor.core.publisher.MonoCreate$DefaultMonoSink.success(MonoCreate.java:177) ~[reactor-core-3.8.7.jar!/:3.8.7]
                at reactor.netty.http.client.HttpClientConnect$HttpIOHandlerObserver.onStateChange(HttpClientConnect.java:460) ~[reactor-netty-http-1.3.7.jar!/:1.3.7]
                at reactor.netty.ReactorNetty$CompositeConnectionObserver.onStateChange(ReactorNetty.java:736) ~[reactor-netty-core-1.3.7.jar!/:1.3.7]
                at reactor.netty.resources.DefaultPooledConnectionProvider$DisposableAcquire.onStateChange(DefaultPooledConnectionProvider.java:229) ~[reactor-netty-core-1.3.7.jar!/:1.3.7]
                at reactor.netty.resources.DefaultPooledConnectionProvider$PooledConnection.onStateChange(DefaultPooledConnectionProvider.java:491) ~[reactor-netty-core-1.3.7.jar!/:1.3.7]
                at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:858) ~[reactor-netty-http-1.3.7.jar!/:1.3.7]
                at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:115) ~[reactor-netty-core-1.3.7.jar!/:1.3.7]
                at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:434) ~[netty-transport-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:361) ~[netty-codec-base-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:348) ~[netty-codec-base-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:470) ~[netty-codec-base-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296) ~[netty-codec-base-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:249) ~[netty-transport-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355) ~[netty-transport-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1429) ~[netty-transport-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:918) ~[netty-transport-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:804) ~[netty-transport-classes-epoll-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.handle(AbstractEpollChannel.java:487) ~[netty-transport-classes-epoll-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.channel.epoll.EpollIoHandler$DefaultEpollIoRegistration.handle(EpollIoHandler.java:349) ~[netty-transport-classes-epoll-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.channel.epoll.EpollIoHandler.processReady(EpollIoHandler.java:548) ~[netty-transport-classes-epoll-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.channel.epoll.EpollIoHandler.run(EpollIoHandler.java:491) ~[netty-transport-classes-epoll-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.channel.SingleThreadIoEventLoop.runIo(SingleThreadIoEventLoop.java:225) ~[netty-transport-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.channel.SingleThreadIoEventLoop.run(SingleThreadIoEventLoop.java:196) ~[netty-transport-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:1204) ~[netty-common-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.2.17.Final.jar!/:4.2.17.Final]
                at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.2.17.Final.jar!/:4.2.17.Final]
                at java.base/java.lang.Thread.run(Unknown Source) ~[na:na]

2026-08-26T08:37:40.466Z ERROR 1 --- [gateway] [or-http-epoll-3] o.s.w.s.adapter.HttpWebHandlerAdapter    : [37da73a0-12] Error [java.lang.UnsupportedOperationException] for HTTP GET "/AAA/BBB", but ServerHttpResponse already committed (200 OK)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions