There is no `fireHandlerXXX` method in `ChannelHandlerContext`, only `fireChannelXXX` method. If you want to propagate handler Removed event, you can try `fireUserEventTriggered`.

 My understanding of channelReadComplete () may not be sufficient.

If it is a valid specification that channelReadComplete () is called when deleting a handler in port unification, close this issue.
The behavior was unexpected just for me. Thanks @lifeinwild, I think you're right that it would be better for the removal not to trigger this unless the handler is mid-read. I see that @normanmaurer has opened #9211 to address it. Yes I did fix it... closing
readComplete when handlerRemoved
### Expected behavior
dont call fireChannelReadComplete
call fireHandlerRemoved or nothing

### Actual behavior
call fireChannelReadComplete

https://github.com/netty/netty/blob/a33200ca38990b88315a48637e5ac5da398b100d/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java
line 255
>ctx.fireChannelReadComplete();

### Steps to reproduce
handler is removed in port unification
https://netty.io/4.1/xref/io/netty/example/portunification/PortUnificationServerHandler.html
>p.remove(this);

the removing handler has nothing to do with readComplete.

### Minimal yet complete reproducer code (or URL to code)
you can reproduce it anytime by removing handler.

### Netty version
4.1.31

### JVM version (e.g. `java -version`)
corretto 8

### OS version (e.g. `uname -a`)
win 10
codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java
codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java
diff --git a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java
index bed1efc211c..0457a93abdc 100644
--- a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java
+++ b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java
@@ -241,18 +241,16 @@ public final void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
         if (buf != null) {
             // Directly set this to null so we are sure we not access it in any other method here anymore.
             cumulation = null;
-
+            numReads = 0;
             int readable = buf.readableBytes();
             if (readable > 0) {
                 ByteBuf bytes = buf.readBytes(readable);
                 buf.release();
                 ctx.fireChannelRead(bytes);
+                ctx.fireChannelReadComplete();
             } else {
                 buf.release();
             }
-
-            numReads = 0;
-            ctx.fireChannelReadComplete();
         }
         handlerRemoved0(ctx);
     }
