{"record":{"id":"06c2426bcd1b3afa","repo":"Tencent/tinker","slug":"patch-s-extract-failed-s-06c242","errorCode":null,"errorMessage":"patch %s extract failed (%s).","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/ResDiffPatchInternal.java","lineNumber":242,"sourceCode":"                IOHelper.closeQuietly(out);\n                IOHelper.closeQuietly(oldApk);\n                IOHelper.closeQuietly(newApk);\n\n                //delete temp files\n                SharePatchFileUtil.deleteDir(tempResFileDirectory);\n            }\n            boolean result = SharePatchFileUtil.checkResourceArscMd5(resOutput, resPatchInfo.resArscMd5);\n\n            if (!result) {\n                ShareTinkerLog.i(TAG, \"check final new resource file fail path:%s, entry count:%d, size:%d\", resOutput.getAbsolutePath(), totalEntryCount, resOutput.length());\n                SharePatchFileUtil.safeDeleteFile(resOutput);\n                manager.getPatchReporter().onPatchTypeExtractFail(patchFile, resOutput, ShareConstants.RES_NAME, type);\n                return false;\n            }\n\n            ShareTinkerLog.i(TAG, \"final new resource file:%s, entry count:%d, size:%d\", resOutput.getAbsolutePath(), totalEntryCount, resOutput.length());\n        } catch (Throwable e) {\n            throw new TinkerRuntimeException(\"patch \" + ShareTinkerInternals.getTypeString(type) +  \" extract failed (\" + e.getMessage() + \").\", e);\n        }\n        return true;\n    }\n\n    private static boolean checkAndExtractResourceLargeFile(Context context, String apkPath, File directory, File tempFileDirtory,\n                                                            File patchFile, ShareResPatchInfo resPatchInfo, int type, boolean useCustomPatcher) {\n        long start = System.currentTimeMillis();\n        Tinker manager = Tinker.with(context);\n        ZipFile apkFile = null;\n        ZipFile patchZipFile = null;\n        try {\n            //recover resources.arsc first\n            apkFile = new ZipFile(apkPath);\n            ZipEntry arscEntry = apkFile.getEntry(ShareConstants.RES_ARSC);\n            File arscFile = new File(directory, ShareConstants.RES_ARSC);\n            if (arscEntry == null) {\n                ShareTinkerLog.w(TAG, \"resources apk entry is null. path:\" + ShareConstants.RES_ARSC);\n                manager.getPatchReporter().onPatchTypeExtractFail(patchFile, arscFile, ShareConstants.RES_ARSC, type);","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/ResDiffPatchInternal.java#L224-L260","documentation":"ResDiffPatchInternal's catch-all: any Throwable thrown while reconstructing the new resources file (merging old APK entries with added/modified/large-modified resources and re-checking resources.arsc MD5) is wrapped in a TinkerRuntimeException tagged with the resource patch type. Checksum mismatches alone do not throw this (they report via onPatchTypeExtractFail and return false); this exception means an unexpected Throwable escaped the reconstruction logic.","triggerScenarios":"Applying a resource patch when TinkerZipOutputStream write fails (disk full, resOutput set read-only on API 33+ path), an entry extraction from oldApk/newApk TinkerZipFile throws, or checkResourceArscMd5's file I/O throws. Also reached if the arsc patch application inside this try block raises.","commonSituations":"Disk exhaustion on low-end devices; patch built against a different base APK so entry lookups misbehave; API 33+ read-only resOutput interplay with the custom zip writer; resources.arsc changes too large for the in-place merge strategy.","solutions":["Free device storage / verify the tinker patch directory is writable before applying.","Confirm the patch was generated from the exact installed base APK (same tinkerId and resource table).","Inspect the wrapped cause via PatchReporter.onPatchException to distinguish I/O from format failures.","Re-download and re-verify the patch file MD5 before applying.","For very large resource changes, ship a full APK upgrade instead of a res patch."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (context.getFilesDir().getFreeSpace() < requiredSpaceForResPatch) {\n    deferPatchApply(\"low storage\");\n}\nif (!SharePatchFileUtil.verifyFileMd5(new File(patchPath), serverPayload.patchMd5)) {\n    deferPatchApply(\"bad patch file\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    TinkerInstaller.onReceiveUpgradePatch(context, patchPath);\n} catch (TinkerRuntimeException e) {\n    Throwable cause = e.getCause() != null ? e.getCause() : e;\n    if (cause instanceof IOException) { scheduleRetryAfterCleanup(); }\n    else { reportPatchFormatFailure(cause); }\n}","preventionTips":["Verify patch MD5 and free storage before every apply.","Ensure the patch was built from the same base APK (tinkerId check) before distributing.","Clean the tinker temp directory between failed retries so partial outputs do not accumulate."],"tags":["tinker","android","resources","arsc","io","zip"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}