Tencent/tinker · error · ZipException
No entries
Error message
No entries
What it means
TinkerZipOutputStream.finish() finalizes the archive by writing the central directory and end-of-central-directory records. A zip archive with zero entries is rejected with ZipException("No entries") because Tinker's repack flow (used to rebuild APK/zip files during patch generation) treats an empty central directory as an invalid output. close() also triggers this since it calls finish().
Source
Thrown at third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipOutputStream.java:381
/**
* Indicates that all entries have been written to the stream. Any terminal
* information is written to the underlying stream.
*
* @throws IOException
* if an error occurs while terminating the stream.
*/
// @Override
public void finish() throws IOException {
// TODO: is there a bug here? why not checkOpen?
if (out == null) {
throw new IOException("Stream is closed");
}
if (cDir == null) {
return;
}
if (entries.isEmpty()) {
throw new ZipException("No entries");
}
if (currentEntry != null) {
closeEntry();
}
int cdirEntriesSize = cDir.size();
/*if (archiveNeedsZip64EocdRecord) {
Zip64.writeZip64EocdRecordAndLocator(cDir, entries.size(), offset, cdirEntriesSize);
}*/
// Write Central Dir End
writeLongAsUint32(cDir, ENDSIG);
writeIntAsUint16(cDir, 0); // Disk Number
writeIntAsUint16(cDir, 0); // Start Disk
// Instead of trying to figure out *why* this archive needed a zip64 eocd record,
// just delegate all these values to the zip64 eocd record.
if (archiveNeedsZip64EocdRecord) {
writeIntAsUint16(cDir, 0xFFFF); // Number of entries
writeIntAsUint16(cDir, 0xFFFF); // Number of entries
writeLongAsUint32(cDir, 0xFFFFFFFF); // Size of central dirView on GitHub (pinned to 1b7ea02c23)
Solutions
- Ensure at least one entry is added via putNextEntry() before calling finish()/close(); track a counter in the copy loop and verify it is > 0.
- If an empty archive is legitimately possible in your flow, catch ZipException around finish() and handle it explicitly (e.g. delete the empty output file and skip it).
- Debug why the entry-copy loop produced zero entries: log source entry names and the filter conditions before writing.
Example fix
// before
for (TinkerZipEntry e : src.entries()) {
if (shouldCopy(e)) copyOne(zos, e);
}
zos.finish(); // throws if nothing copied
// after
int copied = 0;
for (TinkerZipEntry e : src.entries()) {
if (shouldCopy(e)) { copyOne(zos, e); copied++; }
}
if (copied == 0) {
throw new IllegalStateException("no entries copied from " + srcName);
}
zos.finish(); Defensive patterns
Strategy: validation
Validate before calling
// before finishing, track how many entries were written
int entriesWritten = 0;
// ... in copy loop, after each successful putNextEntry/closeEntry:
// entriesWritten++;
if (entriesWritten == 0) {
throw new IllegalStateException("refusing to finish empty archive " + outPath);
}
zos.finish(); Try / catch
// only if empty archives are acceptable in your flow
try {
zos.finish();
} catch (ZipException e) {
if ("No entries".equals(e.getMessage())) {
// handle deliberately-empty output (e.g. delete partial file)
} else { throw e; }
} Prevention
- Count entries as you add them and check the count before finish()
- Log source entry counts before starting a repack so zero-entry inputs are visible
- Treat a zero-entry result from a copy loop as a data problem, not an empty-archive feature
When it happens
Trigger: Calling finish() or close() on a TinkerZipOutputStream without any successful putNextEntry() call — e.g. a copy loop over a source zip that matched zero entries, or all putNextEntry calls being skipped by a filter, or an exception aborting the loop before the first entry was written and the caller still closing the stream.
Common situations: Repacking an APK where a filter (e.g. exclude patterns for META-INF signatures) excludes every entry; iterating source entries that fail an md5/name check so nothing is copied; an empty input zip passed into Tinker's zip utils during patch build.
Related errors
- STORED entry missing CRC
- STORED entry missing size
- STORED entry size/compressed size mismatch
- No active entry
- ${property} too long in UTF-8:${bytes.length} bytes
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/c25aeccf7dcb2df0.
Report an issue: GitHub.