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
- 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.
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
- 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.
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
- Entry already exists: {}
- file name:${filename}, file size${fileSize}, entry name:${en
- Not a zip archive
- Local file header offset is after central directory
- zipEntry is null when get from oldApk
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/d234f7f55463326d.
Report an issue: GitHub.