{"record":{"id":"c25aeccf7dcb2df0","repo":"Tencent/tinker","slug":"no-entries","errorCode":null,"errorMessage":"No entries","messagePattern":"No entries","errorType":"exception","errorClass":"ZipException","httpStatus":null,"severity":"error","filePath":"third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipOutputStream.java","lineNumber":381,"sourceCode":"\n    /**\n     * Indicates that all entries have been written to the stream. Any terminal\n     * information is written to the underlying stream.\n     *\n     * @throws IOException\n     *             if an error occurs while terminating the stream.\n     */\n    // @Override\n    public void finish() throws IOException {\n        // TODO: is there a bug here? why not checkOpen?\n        if (out == null) {\n            throw new IOException(\"Stream is closed\");\n        }\n        if (cDir == null) {\n            return;\n        }\n        if (entries.isEmpty()) {\n            throw new ZipException(\"No entries\");\n        }\n        if (currentEntry != null) {\n            closeEntry();\n        }\n        int cdirEntriesSize = cDir.size();\n        /*if (archiveNeedsZip64EocdRecord) {\n            Zip64.writeZip64EocdRecordAndLocator(cDir, entries.size(), offset, cdirEntriesSize);\n        }*/\n        // Write Central Dir End\n        writeLongAsUint32(cDir, ENDSIG);\n        writeIntAsUint16(cDir, 0); // Disk Number\n        writeIntAsUint16(cDir, 0); // Start Disk\n        // Instead of trying to figure out *why* this archive needed a zip64 eocd record,\n        // just delegate all these values to the zip64 eocd record.\n        if (archiveNeedsZip64EocdRecord) {\n            writeIntAsUint16(cDir, 0xFFFF); // Number of entries\n            writeIntAsUint16(cDir, 0xFFFF); // Number of entries\n            writeLongAsUint32(cDir, 0xFFFFFFFF); // Size of central dir","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipOutputStream.java#L363-L399","documentation":"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().","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nfor (TinkerZipEntry e : src.entries()) {\n    if (shouldCopy(e)) copyOne(zos, e);\n}\nzos.finish(); // throws if nothing copied\n\n// after\nint copied = 0;\nfor (TinkerZipEntry e : src.entries()) {\n    if (shouldCopy(e)) { copyOne(zos, e); copied++; }\n}\nif (copied == 0) {\n    throw new IllegalStateException(\"no entries copied from \" + srcName);\n}\nzos.finish();","handlingStrategy":"validation","validationCode":"// before finishing, track how many entries were written\nint entriesWritten = 0;\n// ... in copy loop, after each successful putNextEntry/closeEntry:\n//   entriesWritten++;\nif (entriesWritten == 0) {\n    throw new IllegalStateException(\"refusing to finish empty archive \" + outPath);\n}\nzos.finish();","typeGuard":null,"tryCatchPattern":"// only if empty archives are acceptable in your flow\ntry {\n    zos.finish();\n} catch (ZipException e) {\n    if (\"No entries\".equals(e.getMessage())) {\n        // handle deliberately-empty output (e.g. delete partial file)\n    } else { throw e; }\n}","preventionTips":["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"],"tags":["zip","java","tinker","android","archive"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}