Tencent/tinker · error · TinkerRuntimeException

patch %s extract failed (%s).

Error message

patch %s extract failed (%s).

What it means

Thrown by DexDiffPatchInternal when any Throwable escapes the dex recovery loop (extracting, repacking, bsdiff/dex-patch applying, and class-N dex merging) during patch application. The exception message embeds the patch type ('dex') and the original throwable's message. It is a catch-all wrapper: the real failure is the wrapped cause.

Source

Thrown at tinker-android/tinker-android-lib/src/main/java/com/tencent/tinker/lib/patch/DexDiffPatchInternal.java:626

                    patchDexFile(apk, patch, rawApkFileEntry, patchFileEntry, info, extractedFile);

                    if (!SharePatchFileUtil.verifyDexFileMd5(extractedFile, extractedFileMd5)) {
                        ShareTinkerLog.w(TAG, "Failed to recover dex file when verify patched dex: " + extractedFile.getPath());
                        manager.getPatchReporter().onPatchTypeExtractFail(patchFile, extractedFile, info.rawName, type);
                        SharePatchFileUtil.safeDeleteFile(extractedFile);
                        return false;
                    }

                    ShareTinkerLog.w(TAG, "success recover dex file: %s, size: %d, use time: %d",
                        extractedFile.getPath(), extractedFile.length(), (System.currentTimeMillis() - start));
                }
            }
            if (!mergeClassNDexFiles(context, patchFile, dir)) {
                return false;
            }
        } catch (Throwable e) {
            throw new TinkerRuntimeException("patch " + ShareTinkerInternals.getTypeString(type) + " extract failed (" + e.getMessage() + ").", e);
        } finally {
            SharePatchFileUtil.closeZip(apk);
            SharePatchFileUtil.closeZip(patch);
        }
        return true;
    }

    /**
     * repack dex to jar
     *
     * @param zipFile
     * @param entryFile
     * @param extractTo
     * @param targetMd5
     * @return boolean
     * @throws IOException
     */
    private static boolean extractDexToJar(ZipFile zipFile, ZipEntry entryFile, File extractTo, String targetMd5) throws IOException {

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Ensure the tinkerId of the installed base APK exactly matches the one used to build the patch (check ShareTinkerInternals/getManifestMeta or your build config).
  2. Verify the patch file MD5 before applying to rule out a truncated or tampered download.
  3. Match tinker gradle plugin and lib versions between patch build and app runtime.
  4. Inspect the wrapped cause in the TinkerRuntimeException (and your PatchReporter.onPatchException callback) to identify whether it is dex format, zip, or memory related.
  5. For large multi-dex apps, apply patches while the app is idle and memory pressure is low; consider tinker's dex split config.

Example fix

// before
TinkerInstaller.onReceiveUpgradePatch(context, patchFile);

// after
try {
    TinkerInstaller.onReceiveUpgradePatch(context, patchFile);
} catch (TinkerRuntimeException e) {
    Throwable cause = e.getCause() != null ? e.getCause() : e;
    reportToServer(cause); // keep the wrapped cause, not just the message
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify base APK tinkerId matches the patch's before applying
String currentId = ShareTinkerInternals.getTinkerId(context);
if (currentId == null || !currentId.equals(serverPayload.baseTinkerId)) {
    refusePatch("tinkerId mismatch");
}
if (!SharePatchFileUtil.verifyFileMd5(new File(patchPath), serverPayload.patchMd5)) {
    refusePatch("patch md5 mismatch");
}

Try / catch

try {
    TinkerInstaller.onReceiveUpgradePatch(context, patchPath);
} catch (Throwable t) {
    Throwable cause = (t instanceof TinkerRuntimeException && t.getCause() != null) ? t.getCause() : t;
    patchReporter.onPatchException(cause);
}

Prevention

When it happens

Trigger: Applying a dex diff patch when: the old dex stream in the base APK is corrupted or not the expected format for DexPatchApplier, the patch stream is malformed, a ZipException occurs reading dex-in-jar entries, an OutOfMemoryError occurs on huge dex files, or mergeClassNDexFiles throws while combining classesN.dex files.

Common situations: Patch built against a different base APK than the one installed (tinkerId mismatch); patch generated by an incompatible tinker version; multidex APKs where classes2.dex/N layout changed between build and apply; proguard/r8 mapping drift changing dex contents; low-memory devices applying large multi-dex patches.

Related errors


AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14). Data as JSON: /api/errors/460d0b24b6f1b635. Report an issue: GitHub.