{"record":{"id":"3a7172ece613d3a8","repo":"Tencent/tinker","slug":"can-t-recognize-zip-dex-format-file-s","errorCode":null,"errorMessage":"can't recognize zip dex format file:%s","messagePattern":"can't recognize zip dex format file:(.+?)","errorType":"exception","errorClass":"TinkerRuntimeException","httpStatus":null,"severity":"error","filePath":"tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/DexDiffPatchInternal.java","lineNumber":758,"sourceCode":"            if (!isRawDexFile || patchInfo.isJarMode) {\n                ZipOutputStream zos = null;\n                try {\n                    if (patchedDexFile.exists()) {\n                        patchedDexFile.delete();\n                    }\n                    zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(patchedDexFile)));\n                    zos.putNextEntry(new ZipEntry(ShareConstants.DEX_IN_JAR));\n                    // Old dex is not a raw dex file.\n                    if (!isRawDexFile) {\n                        ZipInputStream zis = null;\n                        try {\n                            zis = new ZipInputStream(oldDexStream);\n                            ZipEntry entry;\n                            while ((entry = zis.getNextEntry()) != null) {\n                                if (ShareConstants.DEX_IN_JAR.equals(entry.getName())) break;\n                            }\n                            if (entry == null) {\n                                throw new TinkerRuntimeException(\"can't recognize zip dex format file:\" + patchedDexFile.getAbsolutePath());\n                            }\n                            new DexPatchApplier(zis, patchFileStream).executeAndSaveTo(zos);\n                        } finally {\n                            IOHelper.closeQuietly(zis);\n                        }\n                    } else {\n                        new DexPatchApplier(oldDexStream, patchFileStream).executeAndSaveTo(zos);\n                    }\n                    zos.closeEntry();\n                } finally {\n                    IOHelper.closeQuietly(zos);\n                }\n            } else {\n                new DexPatchApplier(oldDexStream, patchFileStream).executeAndSaveTo(patchedDexFile);\n            }\n        } finally {\n            IOHelper.closeQuietly(oldDexStream);\n            IOHelper.closeQuietly(patchFileStream);","sourceCodeStart":740,"sourceCodeEnd":776,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/DexDiffPatchInternal.java#L740-L776","documentation":"Thrown while repacking a patched dex: the old dex in the base APK was detected as a ZIP container (not a raw dex), but the code could not find a 'classes.dex' entry inside it after walking all entries with ZipInputStream. Tinker assumes any non-raw dex is a jar whose dex payload lives under the DEX_IN_JAR ('classes.dex') entry; when that assumption fails, the old-dex stream is unrecognizable and patching cannot proceed.","triggerScenarios":"DexDiffPatchInternal tries the non-raw-dex branch (isRawDexFile == false, typically dex files minified/renamed inside the APK such as 'assets/...' jar-packed dex) and ZipInputStream.getNextEntry() exhausts the stream without any entry named exactly 'classes.dex'.","commonSituations":"Base APK processed by a packer/channel tool (360 Jiagu, Bangcle, Ijiami) or an aggressive optimizer that repacks or encrypts dex containers; patch generated from the pre-obfuscated APK but applied against a repacked distribution APK; entry renamed to something other than classes.dex inside the jar.","solutions":["Regenerate the patch using the exact APK that is actually distributed and installed (same channel/repack step), not a pre-processing build.","Avoid dex-encrypting packers on builds intended to receive tinker dex patches, or exclude dex patching (tinkerFlags without TINKER_DEX) for those channels.","Verify the old APK's dex container manually: unzip the base APK, open the jar-packed dex, and confirm a 'classes.dex' entry exists.","If dex entries are renamed by your build, keep the canonical 'classes.dex' name so tinker can find it."],"exampleFix":"// before (build.gradle): patch built from the un-repacked release apk\n// apk: build/outputs/apk/release/app-release.apk\n\n// after: build the patch from the exact distributed artifact\n// apk: dist/app-release-jiagu-signed.apk  (after channel/repack tool, aligned+signed)","handlingStrategy":"validation","validationCode":"// Before distributing dex patches, sanity-check the base APK's jar-packed dex\nZipFile apk = new ZipFile(baseApk);\n// for each dex tinker will treat as non-raw, confirm classes.dex exists inside\nZipInputStream zis = new ZipInputStream(apk.getInputStream(dexEntry));\nZipEntry e; boolean found = false;\nwhile ((e = zis.getNextEntry()) != null) {\n    if (\"classes.dex\".equals(e.getName())) { found = true; break; }\n}\nif (!found) throw new IllegalStateException(\"base apk dex container not tinker-compatible\");","typeGuard":null,"tryCatchPattern":"try {\n    TinkerInstaller.onReceiveUpgradePatch(context, patchPath);\n} catch (TinkerRuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"can't recognize zip dex format\")) {\n        // base APK dex container incompatible: fall back to full upgrade, do not retry this patch\n        markChannelForFullUpgrade();\n    }\n}","preventionTips":["Build patches from the exact distributed APK (after channel/repack/packing tools run).","Do not ship dex patches for APKs processed by dex-encrypting packers; disable TINKER_DEX for those channels.","Add a CI step that opens the release APK and asserts the classes.dex entry exists in any jar-packed dex."],"tags":["tinker","android","dex","zip","packer","format"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}