{"record":{"id":"6cc32e46ab4603fc","repo":"Tencent/tinker","slug":"stream-is-closed","errorCode":null,"errorMessage":"Stream is closed","messagePattern":"Stream is closed","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/AlignedZipOutputStream.java","lineNumber":481,"sourceCode":"        if (currentEntry.getMethod() == STORED) {\r\n            out.write(buffer, offset, byteCount);\r\n        } else {\r\n            super.write(buffer, offset, byteCount);\r\n        }\r\n        crc.update(buffer, offset, byteCount);\r\n        crcDataSize += byteCount;\r\n    }\r\n\r\n    private void checkOffsetAndCount(int arrayLength, int offset, int count) {\r\n        if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) {\r\n            throw new ArrayIndexOutOfBoundsException(\"length=\" + arrayLength + \"; regionStart=\" + offset\r\n                    + \"; regionLength=\" + count);\r\n        }\r\n    }\r\n\r\n    private void checkOpen() throws IOException {\r\n        if (closed) {\r\n            throw new IOException(\"Stream is closed\");\r\n        }\r\n    }\r\n}\r\n","sourceCodeStart":463,"sourceCodeEnd":485,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/AlignedZipOutputStream.java#L463-L485","documentation":"checkOpen() guards every mutating operation of AlignedZipOutputStream after close() has run; once the closed flag is set, any further putNextEntry, write, or closeEntry throws IOException(\"Stream is closed\"). This is standard use-after-close protection for stream resources, preventing corruption of the already-finalized central directory.","triggerScenarios":"Calling any write-side method after close() — common when a finally block closes the stream but a queued/lazy writer, callback, or retry path still holds a reference and writes later.","commonSituations":"Utility methods that close the stream they were handed (e.g. a copy() helper closing the output) followed by the caller writing a footer; parallel or deferred tasks (async flushers) outliving the stream; shared streams closed by one code path while another continues appending entries.","solutions":["Establish single ownership of the stream: only the owner closes it, and helpers must not close resources they did not create.","Track stream state in the caller (or wrap with a guard object exposing isOpen()) and check before writing after any operation that may close.","For deferred writers, keep pending data buffered and write before close in a single serialized phase."],"exampleFix":"// before: helper closes the stream, caller keeps writing\nvoid copyAndClose(InputStream in, OutputStream out) throws IOException {\n    ...; out.close();\n}\ncopyAndClose(in, zos);\nzos.putNextEntry(new ZipEntry(\"later\")); // IOException: Stream is closed\n\n// after: helper does not close a stream it does not own\nvoid copy(InputStream in, OutputStream out) throws IOException { ... }\ncopy(in, zos);\nzos.putNextEntry(new ZipEntry(\"later\"));\nzos.close(); // owner closes exactly once, last","handlingStrategy":"validation","validationCode":"// Track lifecycle in the caller and check before writing\nif (zipClosed) {\n    zos = new AlignedZipOutputStream(new java.io.FileOutputStream(outFile, false));\n    zipClosed = false;\n}\nzos.putNextEntry(entry);","typeGuard":null,"tryCatchPattern":"try {\n    zos.write(buffer, 0, n);\n} catch (java.io.IOException e) {\n    if (\"Stream is closed\".equals(e.getMessage())) {\n        // reopen the archive from scratch; partial archives are not resumable\n        zos = new AlignedZipOutputStream(new java.io.FileOutputStream(outFile, false));\n        restartWrite();\n    } else {\n        throw e;\n    }\n}","preventionTips":["Give the stream a single owner responsible for close().","Never let helper methods close streams they did not open.","Serialize all entry writes before close — no deferred/async writers holding the stream."],"tags":["zip","stream-lifecycle","use-after-close","tinker"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}