{"record":{"id":"90dbe621780fbf7f","repo":"Tencent/tinker","slug":"patch-type-extract-failed-message","errorCode":null,"errorMessage":"patch ${type} extract failed (${message}).","messagePattern":"patch (.+?) extract failed \\((.+?)\\)\\.","errorType":"exception","errorClass":"TinkerRuntimeException","httpStatus":null,"severity":"error","filePath":"tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/ArkHotDiffPatchInternal.java","lineNumber":87,"sourceCode":"                File extractedFile = new File(dir + info.name);\n                if (extractedFile.exists()) {\n                    if (md5.equals(SharePatchFileUtil.getMD5(extractedFile))) {\n                        continue;\n                    } else {\n                        extractedFile.delete();\n                    }\n                } else {\n                    extractedFile.getParentFile().mkdirs();\n                }\n\n                ZipEntry patchFileEntry = patch.getEntry(patchRealPath);\n                if (!extract(patch, patchFileEntry, extractedFile, md5, false)) {\n                    manager.getPatchReporter().onPatchTypeExtractFail(patchFile, extractedFile, info.name, type);\n                    return false;\n                }\n            }\n        } catch (IOException e) {\n            throw new TinkerRuntimeException(\"patch \" + ShareTinkerInternals.getTypeString(type)\n                    + \" extract failed (\" + e.getMessage() + \").\", e);\n        } finally {\n            SharePatchFileUtil.closeZip(patch);\n        }\n\n        return true;\n    }\n\n    private static boolean patchArkHotLibraryExtract(Context context, String patchVersionDirectory,\n                                                     String meta, File patchFile) {\n        String dir = patchVersionDirectory + \"/\" + ShareConstants.ARKHOTFIX_PATH + \"/\";\n\n        arkPatchList.clear();\n        ShareArkHotDiffPatchInfo.parseDiffPatchInfo(meta, arkPatchList);\n\n        if (!extractArkHotLibrary(context, dir, patchFile, TYPE_ARKHOT_SO)) {\n            return false;\n        }","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/ArkHotDiffPatchInternal.java#L69-L105","documentation":"Thrown by ArkHotDiffPatchInternal when an IOException escapes while extracting ArkHot (ArkAPK hotfix) patch entries from the patch ZIP into the patch version directory. The raw IOException is wrapped in a TinkerRuntimeException whose message is prefixed with the patch type string ('ark hot' or similar). It signals an I/O-level failure in patch package extraction, not a checksum or format mismatch (those are reported via onPatchTypeExtractFail).","triggerScenarios":"Calling the ArkHot patch apply path (patchArkHotLibraryExtract / extractValidFile with the ark hot patch type) when: the patch ZIP entry for the ark so/dex cannot be read (patch.getEntry returns a broken stream), the destination file under patchVersionDirectory/arkhot/ cannot be created (parent dirs unwritable, disk full), or the ZIP stream is closed mid-read.","commonSituations":"Patch file truncated by an interrupted download; device storage exhausted or /data/data quota hit; patch directory permission changed after a backup/restore; patch package generated with a mismatched ArkHot layout (built against a different base APK or non-Ark build).","solutions":["Verify the downloaded patch file's MD5 against the value distributed by the server before calling TinkerInstaller.onReceiveUpgradePatch.","Check available disk space and that the patch directory (context.getFilesDir()/tinker) is writable before applying.","Re-download the patch and retry the apply; a truncated ZIP is the most frequent cause.","Confirm the patch was built with the same base APK (same tinkerId) and with ArkHot patching enabled in the gradle plugin.","If it persists, capture the wrapped IOException cause and report it via your PatchReporter implementation."],"exampleFix":"// before\nTinkerInstaller.onReceiveUpgradePatch(context, patchFile);\n\n// after\nString expectedMd5 = serverPayload.patchMd5;\nif (!SharePatchFileUtil.verifyFileMd5(patchFile, expectedMd5)) {\n    // discard and re-download instead of applying a broken patch\n    SharePatchFileUtil.safeDeleteFile(patchFile);\n    return;\n}\nTinkerInstaller.onReceiveUpgradePatch(context, patchFile);","handlingStrategy":"try-catch","validationCode":"String md5 = serverPayload.patchMd5;\nif (!SharePatchFileUtil.verifyFileMd5(new File(patchPath), md5)) {\n    SharePatchFileUtil.safeDeleteFile(new File(patchPath));\n    requestPatchReDownload();\n    return;\n}\nif (context.getCacheDir().getFreeSpace() < MIN_PATCH_SPACE) {\n    notifyUserStorageLow();\n    return;\n}","typeGuard":null,"tryCatchPattern":"try {\n    TinkerInstaller.onReceiveUpgradePatch(context, patchPath);\n} catch (TinkerRuntimeException e) {\n    // e.getCause() is the original IOException\n    reportPatchApplyFailure(e.getCause());\n}","preventionTips":["Always verify the patch file MD5 against the server-published value before applying.","Check free disk space in the tinker patch directory before starting an apply.","Download patches atomically (temp file + rename) so truncated zips are never handed to tinker."],"tags":["tinker","android","hotpatch","arkhot","io","zip"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}