Tencent/tinker · critical · IOException

old dex version mismatch! expetced:

Error message

old dex version mismatch! expetced: 

What it means

IOException from DexPatchApplier.executeAndSaveTo: for patch format v03+, the patch file records the dex API level of the old dex it was generated against, and the runtime old dex's TableOfContents.api differs. The patch cannot be applied because section layout differs between the two dex versions.

Source

Thrown at tinker-commons/src/main/java/com/tencent/tinker/commons/dexpatcher/DexPatchApplier.java:125

            DexPatchFile patchFileIn
    ) {
        this.oldDex = oldDexIn;
        this.patchFile = patchFileIn;
        this.patchedDex = new Dex(patchFileIn.getPatchedDexSize());
        this.oldToPatchedIndexMap = new SparseIndexMap();
    }

    public void executeAndSaveTo(OutputStream out) throws IOException {
        // Before executing, we should check if this patch can be applied to
        // old dex we passed in.
        if (this.patchFile == null) {
            throw new IllegalArgumentException("patch file is null.");
        }
        if (this.patchFile.getVersion() > DexPatchFile.VERSION_02) {
            final int oldDexAPI = this.oldDex.getTableOfContents().api;
            final int expectedOldDexAPI = this.patchFile.getOldDexAPI();
            if (oldDexAPI != expectedOldDexAPI) {
                throw new IOException("old dex version mismatch! expetced: "
                        + expectedOldDexAPI + ", actual: " + oldDexAPI);
            }
        }
        byte[] oldDexSign = this.oldDex.computeSignature(false);
        if (oldDexSign == null) {
            throw new IOException("failed to compute old dex's signature.");
        }
        byte[] oldDexSignInPatchFile = this.patchFile.getOldDexSignature();
        if (CompareUtils.uArrCompare(oldDexSign, oldDexSignInPatchFile) != 0) {
            throw new IOException(
                    String.format(
                            "old dex signature mismatch! expected: %s, actual: %s",
                            Arrays.toString(oldDexSign),
                            Arrays.toString(oldDexSignInPatchFile)
                    )
            );
        }

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Regenerate the patch against the exact base apk currently distributed
  2. Keep build toolchain versions identical between the patch-generation build and the released base package
  3. Enforce base-version checks in your patch distribution service before delivery
Defensive patterns

Strategy: validation

Validate before calling

if (patchFile.getVersion() > DexPatchFile.VERSION_02
        && oldDex.getTableOfContents().api != patchFile.getOldDexAPI()) {
    throw new IOException("refusing to apply: dex api mismatch, regenerate patch for this base");
}

Try / catch

try {
    applier.executeAndSaveTo(out);
} catch (IOException e) {
    if (String.valueOf(e.getMessage()).startsWith("old dex version mismatch")) {
        // signal server to rebuild patch against installed base version
    } else { throw e; }
}

Prevention

When it happens

Trigger: Applying a v03+ dex patch to an old dex whose api field (dex version, e.g., 035 vs 038) differs from getOldDexAPI() recorded at diff time.

Common situations: Base apk rebuilt with a different AGP/compileSdk (changing dex version) after the patch was made; applying a patch to the wrong base version; distributing patches built against a debug build to a release build.

Related errors


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