{"record":{"id":"bc71469818e58e85","repo":"Tencent/tinker","slug":"zip-file-closed","errorCode":null,"errorMessage":"Zip file closed","messagePattern":"Zip file closed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipFile.java","lineNumber":265,"sourceCode":"     */\n    public void close() throws IOException {\n        // guard.close();\n        RandomAccessFile localRaf = raf;\n        if (localRaf != null) { // Only close initialized instances\n            synchronized (localRaf) {\n                raf = null;\n                localRaf.close();\n            }\n            if (fileToDeleteOnClose != null) {\n                fileToDeleteOnClose.delete();\n                fileToDeleteOnClose = null;\n            }\n        }\n    }\n\n    private void checkNotClosed() {\n        if (raf == null) {\n            throw new IllegalStateException(\"Zip file closed\");\n        }\n    }\n\n    /**\n     * Returns an enumeration of the entries. The entries are listed in the\n     * order in which they appear in the zip file.\n     *\n     * <p>If you only need to iterate over the entries in a zip file, and don't\n     * need random-access entry lookup by name, you should probably use {@link ZipInputStream}\n     * instead, to avoid paying to construct the in-memory index.\n     *\n     * @throws IllegalStateException if this zip file has been closed.\n     */\n    public Enumeration<? extends TinkerZipEntry> entries() {\n        checkNotClosed();\n        final Iterator<TinkerZipEntry> iterator = entries.values().iterator();\n        return new Enumeration<TinkerZipEntry>() {\n            public boolean hasMoreElements() {","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipFile.java#L247-L283","documentation":"Every read-style operation on TinkerZipFile (getEntry, getInputStream, getEntryComment, entries(), size()) starts with checkNotClosed(), which throws IllegalStateException('Zip file closed') once the underlying RandomAccessFile has been released. This fires not only after an explicit close() but also after a failed constructor path or a close from another thread. The state is not recoverable — you must open a new TinkerZipFile.","triggerScenarios":"Calling any accessor on a TinkerZipFile instance after close(); racing between one thread closing the file (e.g. try-with-resources scope ending) and another thread still enumerating entries or reading streams obtained from it.","commonSituations":"try-with-resources blocks that are scoped too tightly around long-running stream reads; caching a TinkerZipFile across requests while some path closes it; concurrent shutdown hooks closing shared zip handles.","solutions":["Widen the try-with-resources scope (or defer close) so all reads finish before close() runs.","Serialize close against readers with a lock or lifecycle flag; treat IllegalStateException('Zip file closed') as a signal to reopen, not to retry the same instance.","Do not cache TinkerZipFile beyond the lifetime of the code that reads from it — open, use, close within one owner."],"exampleFix":"// before\nTinkerZipEntry e;\ntry (TinkerZipFile zf = new TinkerZipFile(f)) {\n    e = zf.getEntry(\"classes.dex\");\n} // closed here\nInputStream is = zf.getInputStream(e); // IllegalStateException\n\n// after\ntry (TinkerZipFile zf = new TinkerZipFile(f)) {\n    TinkerZipEntry e = zf.getEntry(\"classes.dex\");\n    try (InputStream is = zf.getInputStream(e)) {\n        // fully consume is here\n    }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    TinkerZipEntry e = zf.getEntry(name);\n} catch (IllegalStateException e) {\n    if (\"Zip file closed\".equals(e.getMessage())) {\n        // not retryable on this instance: reopen or treat as shutdown race\n        handleClosedFile();\n    }\n}","preventionTips":["Scope try-with-resources around the entire read lifecycle, including stream consumption.","Give each TinkerZipFile a single owner; close and read never race when ownership is clear.","Treat this exception as a lifecycle bug to fix, not a condition to retry silently."],"tags":["zip","lifecycle","concurrency","tinker"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}