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

  1. Track written names in your own Set<String> and skip or rename on collision before calling putNextEntry.
  2. When merging archives, define an explicit precedence (e.g. patch files override base files) and filter duplicates in the copy loop.
  3. 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

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


AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14). Data as JSON: /api/errors/58cd798eb8942455. Report an issue: GitHub.