{"record":{"id":"f582781726884e49","repo":"netty/netty","slug":"refcnt-0","errorCode":null,"errorMessage":"refCnt: 0","messagePattern":"refCnt: 0","errorType":"exception","errorClass":"IllegalReferenceCountException","httpStatus":null,"severity":"critical","filePath":"buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java","lineNumber":1480,"sourceCode":"        }\n    }\n\n    private void checkReadableBytes0(int minimumReadableBytes) {\n        ensureAccessible();\n        if (checkBounds && readerIndex > writerIndex - minimumReadableBytes) {\n            throw new IndexOutOfBoundsException(String.format(\n                    \"readerIndex(%d) + length(%d) exceeds writerIndex(%d): %s\",\n                    readerIndex, minimumReadableBytes, writerIndex, this));\n        }\n    }\n\n    /**\n     * Should be called by every method that tries to access the buffers content to check\n     * if the buffer was released before.\n     */\n    protected final void ensureAccessible() {\n        if (checkAccessible && !isAccessible()) {\n            throw new IllegalReferenceCountException(0);\n        }\n    }\n\n    final void setIndex0(int readerIndex, int writerIndex) {\n        this.readerIndex = readerIndex;\n        this.writerIndex = writerIndex;\n    }\n\n    final void discardMarks() {\n        markedReaderIndex = markedWriterIndex = 0;\n    }\n\n    /**\n     * Obtain the memory address without checking {@link #ensureAccessible()} first, if possible.\n     */\n    long _memoryAddress() {\n        return isAccessible() && hasMemoryAddress() ? memoryAddress() : 0L;\n    }","sourceCodeStart":1462,"sourceCodeEnd":1498,"githubUrl":"https://github.com/netty/netty/blob/70040aacae241e9ba371e758f5b86e470bd77ad1/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java#L1462-L1498","documentation":"Thrown by AbstractByteBuf.ensureAccessible as IllegalReferenceCountException(0) when any buffer-accessing method is called on a buffer whose reference count is zero (it has been release()d). ensureAccessible is the universal guard called at the top of nearly every accessor, gated by the io.netty.buffer.checkAccessible flag (default true). A zero count means the buffer's memory may already be returned to its pool, so all further access is forbidden as a use-after-free defense.","triggerScenarios":"Calling any getter/reader/writer (or toString, release again) on a buffer after release() was called once too many times, or after a downstream handler released it (e.g. ctx.writeAndFlush released the buffer, then you touch it).","commonSituations":"Forgetting that writeAndFlush/ChannelPipeline release outbound buffers; releasing a buffer in a finally block AND in a downstream handler (double release); passing a buffer to a codec that retains/releases it while you still hold a reference; autoRead/ref-count bugs in pooled allocators.","solutions":["Call buf.retain() before handing a buffer to code that will release it, if you still need it afterward; release it in your own finally too.","Track ownership explicitly: assign one owner responsible for release(); use try-with-resources via a wrapper or ReferenceCounted semantics.","Add buf.refCnt() / ByteBufUtil.isAccessible(buf) assertions in debug to catch double releases early.","Avoid releasing buffers you did not retain (Netty releases inbound buffers after the pipeline by default)."],"exampleFix":"// before\nbuf.release();\nctx.writeAndFlush(buf); // downstream releases again, then:\nint x = buf.readInt(); // refCnt 0 -> IllegalReferenceCountException\n\n// after\nbuf.retain();\nctx.writeAndFlush(buf);\n// ... later, when truly done:\nbuf.release();","handlingStrategy":"validation","validationCode":"// Check accessibility before any access\nif (!buf.isAccessible()) {\n    // re-acquire or skip; do NOT call any getter on a released buffer\n    return;\n}\nint v = buf.readInt();","typeGuard":"static boolean isAlive(ByteBuf buf) {\n    return buf.refCnt() > 0;\n}","tryCatchPattern":"try {\n    buf.writeInt(v);\n} catch (IllegalReferenceCountException e) {\n    buf = alloc.buffer(); // re-acquire a live buffer\n    buf.writeInt(v);\n}","preventionTips":["retain() before handing a buffer to code that will release it.","Establish one owner responsible for each release().","Run with -Dio.netty.leakDetection.level=PARANOID in staging to find premature releases.","Never release inbound buffers you did not retain; the pipeline releases them."],"tags":["netty","bytebuf","ref-count","use-after-release","memory-lifecycle","resource-leak"],"backgroundTag":null,"analyzedSha":"70040aacae241e9ba371e758f5b86e470bd77ad1","analyzedAt":"2026-08-14T01:29:15.551Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}