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 dir

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. 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.
  2. 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).
  3. 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

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


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