Tencent/tinker · error · ZipException
Entry already exists: {}
Error message
Entry already exists: {} What it means
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.
Source
Thrown at third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/AlignedZipOutputStream.java:328
ze.setCompressedSize(ze.getSize());
} else if (ze.getSize() == -1) {
ze.setSize(ze.getCompressedSize());
}
if (ze.getCrc() == -1) {
throw new ZipException("STORED entry missing CRC");
}
if (ze.getSize() == -1) {
throw new ZipException("STORED entry missing size");
}
if (ze.getSize() != ze.getCompressedSize()) {
throw new ZipException("STORED entry size/compressed size mismatch");
}
}
checkOpen();
if (entries.contains(ze.getName())) {
throw new ZipException("Entry already exists: " + ze.getName());
}
if (entries.size() == 64*1024-1) {
throw new ZipException("Too many entries for the zip file format's 16-bit entry count");
}
nameBytes = ze.getName().getBytes(Charset.forName("UTF-8"));
nameLength = nameBytes.length;
if (nameLength > 0xffff) {
throw new IllegalArgumentException("Name too long: " + nameLength + " UTF-8 bytes");
}
def.setLevel(compressionLevel);
ze.setMethod(method);
currentEntry = ze;
entries.add(currentEntry.getName());
// Local file header.
// http://www.pkware.com/documents/casestudies/APPNOTE.TXT
View on GitHub (pinned to 1b7ea02c23)
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.
Example fix
// before: blind copy loop can add the same name twice
for (ZipEntry e : srcZipEntries) {
zos.putNextEntry(new ZipEntry(e.getName())); // throws on duplicate
copy(e);
}
// after: dedupe with a seen-set and explicit precedence
Set<String> seen = new HashSet<>();
for (ZipEntry e : srcZipEntries) {
if (!seen.add(e.getName())) continue; // first wins
zos.putNextEntry(new ZipEntry(e.getName()));
copy(e);
} Defensive patterns
Strategy: validation
Validate before calling
// Deduplicate entry names before writing
java.util.Set<String> seen = java.util.Collections.newSetFromMap(new java.util.LinkedHashMap<>());
// in the copy loop:
if (!seen.add(entry.getName())) {
continue; // or apply rename policy
} Try / catch
try {
zos.putNextEntry(newEntry);
} catch (java.util.zip.ZipException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Entry already exists")) {
// resolve the collision explicitly: skip, or write under a suffixed name
newEntry = new java.util.zip.ZipEntry(entry.getName() + "." + Integer.toHexString(counter++));
zos.putNextEntry(newEntry);
} else {
throw e;
}
} Prevention
- 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.
When it happens
Trigger: 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".
Common situations: 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.
Related errors
- Duplicate entry name: ${entryName}
- Expected ${DEX_IN_JAR_NAME} in ${file}
- Cannot merge dex files that do not contain a map
- CRC mismatch
- Size mismatch
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/58cd798eb8942455.
Report an issue: GitHub.