{"record":{"id":"96023f4970e283bd","repo":"oracle/graal","slug":"already-closed","errorCode":null,"errorMessage":"Already closed","messagePattern":"Already closed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/truffle/BinaryOutput.java","lineNumber":627,"sourceCode":"\n        /**\n         * Closes the buffer and frees off-heap allocated resources.\n         */\n        @Override\n        public void close() {\n            if (unmanaged) {\n                UnmanagedMemory.free(address);\n                byteBufferView = null;\n                address = Word.nullPointer();\n                length = 0;\n                unmanaged = false;\n                pos = Integer.MIN_VALUE;\n            }\n        }\n\n        private void checkClosed() {\n            if (pos == Integer.MIN_VALUE) {\n                throw new IllegalStateException(\"Already closed\");\n            }\n        }\n\n        private void ensureCapacity(int neededCapacity) {\n            if (neededCapacity - length > 0) {\n                byteBufferView = null;\n                int newCapacity = length << 1;\n                if (newCapacity - neededCapacity < 0) {\n                    newCapacity = neededCapacity;\n                }\n                if (newCapacity - Integer.MAX_VALUE > 0) {\n                    throw new OutOfMemoryError();\n                }\n                if (unmanaged) {\n                    address = UnmanagedMemory.realloc(address, Word.unsigned(newCapacity));\n                } else {\n                    CCharPointer newAddress = UnmanagedMemory.malloc(newCapacity);\n                    memcpy(newAddress, address, pos);","sourceCodeStart":609,"sourceCodeEnd":645,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/truffle/BinaryOutput.java#L609-L645","documentation":"NativeBinaryOutput uses a sentinel pos == Integer.MIN_VALUE after close()/free to mark the buffer dead. Every write(int)/write(byte[],int,int) calls checkClosed(), which throws IllegalStateException('Already closed') when the sentinel is set. Writing after the unmanaged native memory has been freed is guarded this way because it would otherwise corrupt freed memory.","triggerScenarios":"Calling any write method on a BinaryOutput whose close() already ran — e.g. a try-with-resources block whose body escapes into a callback that writes later, or a cached output used after an error path closed it.","commonSituations":"Error-path cleanup closes the stream, then a finally/catch block still tries to write a status record; shared output reused across requests; async completion writing after the resource was released.","solutions":["Track ownership: null the reference on close and check for null before writing.","Move post-close writes to a fresh BinaryOutput instance instead of reusing the closed one.","Restructure try-with-resources so error reporting happens before the resource closes (write status inside the try, close last).","Catch IllegalStateException around optional trailing writes and re-open the channel if the payload matters."],"exampleFix":"// before\ntry (NativeBinaryOutput out = open()) { ... } finally { out.write(statusByte); }\n// after (write status before close)\ntry (NativeBinaryOutput out = open()) {\n    ...\n    out.write(statusByte);\n}","handlingStrategy":"validation","validationCode":"// Track closure yourself so you never call into the output afterwards\nif (outRef.get() != null) outRef.get().write(b, off, len);","typeGuard":null,"tryCatchPattern":"try {\n    out.write(b, off, len);\n} catch (IllegalStateException e) {\n    if (\"Already closed\".equals(e.getMessage())) {\n        // open a fresh BinaryOutput and rewrite the record\n    }\n}","preventionTips":["Keep all writes inside the try-with-resources body","Null the reference on close","Do error-reporting writes before the resource closes"],"tags":["libgraal","truffle","resource-lifecycle","use-after-close"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}