{"record":{"id":"58cd798eb8942455","repo":"Tencent/tinker","slug":"entry-already-exists","errorCode":null,"errorMessage":"Entry already exists: {}","messagePattern":"Entry already exists: (.+?)","errorType":"exception","errorClass":"ZipException","httpStatus":null,"severity":"error","filePath":"third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/AlignedZipOutputStream.java","lineNumber":328,"sourceCode":"                ze.setCompressedSize(ze.getSize());\r\n            } else if (ze.getSize() == -1) {\r\n                ze.setSize(ze.getCompressedSize());\r\n            }\r\n            if (ze.getCrc() == -1) {\r\n                throw new ZipException(\"STORED entry missing CRC\");\r\n            }\r\n            if (ze.getSize() == -1) {\r\n                throw new ZipException(\"STORED entry missing size\");\r\n            }\r\n            if (ze.getSize() != ze.getCompressedSize()) {\r\n                throw new ZipException(\"STORED entry size/compressed size mismatch\");\r\n            }\r\n        }\r\n\r\n        checkOpen();\r\n\r\n        if (entries.contains(ze.getName())) {\r\n            throw new ZipException(\"Entry already exists: \" + ze.getName());\r\n        }\r\n        if (entries.size() == 64*1024-1) {\r\n            throw new ZipException(\"Too many entries for the zip file format's 16-bit entry count\");\r\n        }\r\n        nameBytes = ze.getName().getBytes(Charset.forName(\"UTF-8\"));\r\n        nameLength = nameBytes.length;\r\n        if (nameLength > 0xffff) {\r\n            throw new IllegalArgumentException(\"Name too long: \" + nameLength + \" UTF-8 bytes\");\r\n        }\r\n\r\n        def.setLevel(compressionLevel);\r\n        ze.setMethod(method);\r\n\r\n        currentEntry = ze;\r\n        entries.add(currentEntry.getName());\r\n\r\n        // Local file header.\r\n        // http://www.pkware.com/documents/casestudies/APPNOTE.TXT\r","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/AlignedZipOutputStream.java#L310-L346","documentation":"AlignedZipOutputStream keeps a set of already-written entry names and refuses a second entry with the same name, because a zip with duplicate local headers is ambiguous for readers and breaks the central-directory contract it maintains. putNextEntry throws this ZipException when entries.contains(ze.getName()) is true. Note the comparison is exact and case/suffix-sensitive.","triggerScenarios":"Two putNextEntry calls with the same name (including entries added across a merge loop), e.g. iterating multiple source zips into one output and both contain \"classes.dex\" or \"META-INF/MANIFEST.MF\".","commonSituations":"Merging an APK with injected files (tinker patch packaging) where a name is added both by copy and by hand; directory-ish name mismatches like \"res/x\" vs \"res/x/\" being treated as different but literal duplicates like two \"META-INF/CERT.SF\" colliding; case-insensitive filesystems masking duplicates during asset preparation that then collide in the zip.","solutions":["Track written names in your own Set<String> and skip or rename on collision before calling putNextEntry.","When merging archives, define an explicit precedence (e.g. patch files override base files) and filter duplicates in the copy loop.","Normalize names before comparison (strip leading './', decide case policy) so intended duplicates are caught early."],"exampleFix":"// before: blind copy loop can add the same name twice\nfor (ZipEntry e : srcZipEntries) {\n    zos.putNextEntry(new ZipEntry(e.getName())); // throws on duplicate\n    copy(e);\n}\n\n// after: dedupe with a seen-set and explicit precedence\nSet<String> seen = new HashSet<>();\nfor (ZipEntry e : srcZipEntries) {\n    if (!seen.add(e.getName())) continue; // first wins\n    zos.putNextEntry(new ZipEntry(e.getName()));\n    copy(e);\n}","handlingStrategy":"validation","validationCode":"// Deduplicate entry names before writing\njava.util.Set<String> seen = java.util.Collections.newSetFromMap(new java.util.LinkedHashMap<>());\n// in the copy loop:\nif (!seen.add(entry.getName())) {\n    continue; // or apply rename policy\n}","typeGuard":null,"tryCatchPattern":"try {\n    zos.putNextEntry(newEntry);\n} catch (java.util.zip.ZipException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Entry already exists\")) {\n        // resolve the collision explicitly: skip, or write under a suffixed name\n        newEntry = new java.util.zip.ZipEntry(entry.getName() + \".\" + Integer.toHexString(counter++));\n        zos.putNextEntry(newEntry);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Maintain your own seen-name set when merging multiple sources into one output.","Normalize names (strip leading './', unify separators) before comparing.","Decide duplicate precedence (first-wins / last-wins) in one place in the packaging code."],"tags":["zip","duplicate-entry","merge","tinker"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}