Tencent/tinker · error · ZipException

Duplicate entry name: ${entryName}

Error message

Duplicate entry name: ${entryName}

What it means

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.

Source

Thrown at third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipFile.java:488

            comment = new String(commentBytes, 0, commentBytes.length, StandardCharsets.UTF_8);
        }

        // Seek to the first CDE and read all entries.
        // We have to do this now (from the constructor) rather than lazily because the
        // public API doesn't allow us to throw IOException except from the constructor
        // or from getInputStream.
        RAFStream rafStream = new RAFStream(raf, centralDirOffset);
        BufferedInputStream bufferedStream = new BufferedInputStream(rafStream, 4096);
        byte[] hdrBuf = new byte[CENHDR]; // Reuse the same buffer for each entry.
        for (int i = 0; i < numEntries; ++i) {
            TinkerZipEntry newEntry = new TinkerZipEntry(hdrBuf, bufferedStream, StandardCharsets.UTF_8,
                (false) /* isZip64 */);
            if (newEntry.localHeaderRelOffset >= centralDirOffset) {
                throw new ZipException("Local file header offset is after central directory");
            }
            String entryName = newEntry.getName();
            if (entries.put(entryName, newEntry) != null) {
                throw new ZipException("Duplicate entry name: " + entryName);
            }
        }

    }

    // private final CloseGuard guard = CloseGuard.get();
    static class EocdRecord {
        final long numEntries;
        final long centralDirOffset;
        final int commentLength;
        EocdRecord(long numEntries, long centralDirOffset, int commentLength) {
            this.numEntries = numEntries;
            this.centralDirOffset = centralDirOffset;
            this.commentLength = commentLength;
        }
    }

    /**

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Rebuild the archive so each name appears exactly once (extract to a directory and re-zip it; directory semantics force uniqueness).
  2. Fix the producing tool: replace-by-name instead of append when updating entries.
  3. 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.

Example fix

// before
// job appends updated entries -> duplicate names
zipCmd.append(archive, updatedFiles);
new TinkerZipFile(archive);

// after
// replace by name: rebuild archive from a unique-name file set
File tmp = extractUnique(archive, updatedFiles);
rezip(tmp, archive);
new TinkerZipFile(archive);
Defensive patterns

Strategy: try-catch

Validate before calling

// when generating archives, enforce unique names at the source
Set<String> seen = new HashSet<>();
for (FileEntry fe : entriesToAdd) {
    if (!seen.add(fe.name)) {
        throw new IllegalStateException("Duplicate entry name would corrupt zip: " + fe.name);
    }
}

Try / catch

try {
    new TinkerZipFile(file);
} catch (ZipException e) {
    if (e.getMessage().startsWith("Duplicate entry name")) {
        String dup = e.getMessage().substring(e.getMessage().indexOf(':') + 1).trim();
        throw new IOException("Archive contains duplicate entry '" + dup + "'; rebuild it", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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