{"record":{"id":"d234f7f55463326d","repo":"Tencent/tinker","slug":"duplicate-entry-name-entryname","errorCode":null,"errorMessage":"Duplicate entry name: ${entryName}","messagePattern":"Duplicate entry name: (.+?)","errorType":"exception","errorClass":"ZipException","httpStatus":null,"severity":"error","filePath":"third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipFile.java","lineNumber":488,"sourceCode":"            comment = new String(commentBytes, 0, commentBytes.length, StandardCharsets.UTF_8);\n        }\n\n        // Seek to the first CDE and read all entries.\n        // We have to do this now (from the constructor) rather than lazily because the\n        // public API doesn't allow us to throw IOException except from the constructor\n        // or from getInputStream.\n        RAFStream rafStream = new RAFStream(raf, centralDirOffset);\n        BufferedInputStream bufferedStream = new BufferedInputStream(rafStream, 4096);\n        byte[] hdrBuf = new byte[CENHDR]; // Reuse the same buffer for each entry.\n        for (int i = 0; i < numEntries; ++i) {\n            TinkerZipEntry newEntry = new TinkerZipEntry(hdrBuf, bufferedStream, StandardCharsets.UTF_8,\n                (false) /* isZip64 */);\n            if (newEntry.localHeaderRelOffset >= centralDirOffset) {\n                throw new ZipException(\"Local file header offset is after central directory\");\n            }\n            String entryName = newEntry.getName();\n            if (entries.put(entryName, newEntry) != null) {\n                throw new ZipException(\"Duplicate entry name: \" + entryName);\n            }\n        }\n\n    }\n\n    // private final CloseGuard guard = CloseGuard.get();\n    static class EocdRecord {\n        final long numEntries;\n        final long centralDirOffset;\n        final int commentLength;\n        EocdRecord(long numEntries, long centralDirOffset, int commentLength) {\n            this.numEntries = numEntries;\n            this.centralDirOffset = centralDirOffset;\n            this.commentLength = commentLength;\n        }\n    }\n\n    /**","sourceCodeStart":470,"sourceCodeEnd":506,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipFile.java#L470-L506","documentation":"As readCentralDir loads each central-directory entry into a name->entry map, a second entry with an identical name is treated as fatal corruption and throws ZipException('Duplicate entry name: <name>') from the constructor. The zip spec nominally forbids duplicate names, but some tools produce them; this library, like Android's ZipFile, refuses the whole archive rather than silently shadowing one entry. The message names the offending entry so you can locate it.","triggerScenarios":"Opening a zip that literally contains two central-directory records with the same name — produced by 'zip archive.zip file' run twice on recreated content, archive-merge tools that concatenate directories, or a hand-built patch stream that appends entries with names already present.","commonSituations":"Incremental packaging jobs that append instead of replacing; merging two APKs' entries; zip bombs crafted with duplicate names; bugs in custom zip writers that emit the CD twice.","solutions":["Rebuild the archive so each name appears exactly once (extract to a directory and re-zip it; directory semantics force uniqueness).","Fix the producing tool: replace-by-name instead of append when updating entries.","If you must consume such archives, preprocess with a tool that keeps the last occurrence (e.g. a repair pass) before handing the file to TinkerZipFile."],"exampleFix":"// before\n// job appends updated entries -> duplicate names\nzipCmd.append(archive, updatedFiles);\nnew TinkerZipFile(archive);\n\n// after\n// replace by name: rebuild archive from a unique-name file set\nFile tmp = extractUnique(archive, updatedFiles);\nrezip(tmp, archive);\nnew TinkerZipFile(archive);","handlingStrategy":"try-catch","validationCode":"// when generating archives, enforce unique names at the source\nSet<String> seen = new HashSet<>();\nfor (FileEntry fe : entriesToAdd) {\n    if (!seen.add(fe.name)) {\n        throw new IllegalStateException(\"Duplicate entry name would corrupt zip: \" + fe.name);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    new TinkerZipFile(file);\n} catch (ZipException e) {\n    if (e.getMessage().startsWith(\"Duplicate entry name\")) {\n        String dup = e.getMessage().substring(e.getMessage().indexOf(':') + 1).trim();\n        throw new IOException(\"Archive contains duplicate entry '\" + dup + \"'; rebuild it\", e);\n    }\n    throw e;\n}","preventionTips":["Update archives by replace-by-name, never by append.","Deduplicate file lists before zipping (a HashSet of names catches it in O(1)).","Treat duplicate names in incoming archives as a producer-tool bug and fix the producer."],"tags":["zip","corruption","duplicate-entry","tinker"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}