{"record":{"id":"69325d0d47125ce8","repo":"Tencent/tinker","slug":"zipentry-is-null-when-get-from-oldapk","errorCode":null,"errorMessage":"zipEntry is null when get from oldApk","messagePattern":"zipEntry is null when get from oldApk","errorType":"exception","errorClass":"TinkerRuntimeException","httpStatus":null,"severity":"error","filePath":"tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/ResDiffPatchInternal.java","lineNumber":150,"sourceCode":"            TinkerZipOutputStream out = null;\n            TinkerZipFile oldApk = null;\n            TinkerZipFile newApk = null;\n            int totalEntryCount = 0;\n            try {\n                if (resOutput.exists()) {\n                    resOutput.delete();\n                }\n                out = new TinkerZipOutputStream(new BufferedOutputStream(new FileOutputStream(resOutput)));\n                if (ShareTinkerInternals.isNewerOrEqualThanVersion(33, true)) {\n                    resOutput.setReadOnly();\n                }\n                oldApk = new TinkerZipFile(apkPath);\n                newApk = new TinkerZipFile(patchFile);\n                final Enumeration<? extends TinkerZipEntry> entries = oldApk.entries();\n                while (entries.hasMoreElements()) {\n                    TinkerZipEntry zipEntry = entries.nextElement();\n                    if (zipEntry == null) {\n                        throw new TinkerRuntimeException(\"zipEntry is null when get from oldApk\");\n                    }\n                    String name = zipEntry.getName();\n                    if (name.contains(\"../\")) {\n                        continue;\n                    }\n                    if (ShareResPatchInfo.checkFileInPattern(resPatchInfo.patterns, name)) {\n                        //won't contain in add set.\n                        if (!resPatchInfo.deleteRes.contains(name)\n                            && !resPatchInfo.modRes.contains(name)\n                            && !resPatchInfo.largeModRes.contains(name)\n                            && !name.equals(ShareConstants.RES_MANIFEST)) {\n                            TinkerZipUtil.extractTinkerEntry(oldApk, zipEntry, out);\n                            totalEntryCount++;\n                        }\n                    }\n                }\n\n                //process manifest","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/ResDiffPatchInternal.java#L132-L168","documentation":"A defensive guard inside ResDiffPatchInternal's resource reconstruction loop: while enumerating entries of the base APK via TinkerZipFile.entries(), an element came back null. TinkerZipFile's enumeration is not expected to yield null, so hitting this indicates the old APK's central directory is damaged or the zip implementation returned an abnormal entry. It aborts resource patching before any output is trusted.","triggerScenarios":"During patchResource with an old APK whose ZIP structure is corrupt (truncated central directory, overlapping entries, or a zip flavor TinkerZipFile iterates abnormally), entries.nextElement() returns null on some iteration.","commonSituations":"Base APK corrupted on disk or by a repack/signing step; APK served through a CDN or hot-fix channel that truncated it; v2-signing-only zips processed by an older zip reader; extremely large resource tables triggering partial-entry reads.","solutions":["Validate the installed base APK integrity (e.g., re-verify its signature or CRC) before distributing resource patches.","Rebuild/re-sign the base APK with standard zipalign + apksigner and confirm it opens cleanly with ZipFile before shipping patches.","Reinstall the base APK on affected devices; local corruption cannot be fixed by a patch.","If unfixable on some devices, disable resource patching (TINKER_DISABLE for res) so dex/so patches still apply."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"try {\n    ZipFile apk = new ZipFile(context.getApplicationInfo().sourceDir);\n    Enumeration<? extends ZipEntry> es = apk.entries();\n    while (es.hasMoreElements()) { es.nextElement(); } // walk to surface corruption early\n    apk.close();\n} catch (IOException e) {\n    // base APK zip is unreadable: skip patching, prompt reinstall\n}","typeGuard":null,"tryCatchPattern":"try {\n    TinkerInstaller.onReceiveUpgradePatch(context, patchPath);\n} catch (TinkerRuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"zipEntry is null\")) {\n        disableResPatching(); // recover with dex/so only next time\n    }\n}","preventionTips":["Distribute base APKs through channels that preserve file integrity (checksummed CDN transfer).","Zipalign and sign release APKs with apksigner; avoid zip post-processing that rewrites central directories.","Treat this exception as base-APK corruption: reinstall the app rather than re-applying patches."],"tags":["tinker","android","resources","zip","corruption"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}