Tencent/tinker · critical · IllegalStateException

bad dex patch file version:

Error message

bad dex patch file version: 

What it means

IllegalStateException from DexPatchFile.init: the magic matched but the patch format version short is neither VERSION_02 nor VERSION_03. The parsing code only understands these two formats, so patches written by a different (newer or older) generator are rejected.

Source

Thrown at tinker-commons/src/main/java/com/tencent/tinker/commons/dexpatcher/struct/DexPatchFile.java:86

    public DexPatchFile(File file) throws IOException {
        this.buffer = new DexDataBuffer(ByteBuffer.wrap(FileUtils.readFile(file)));
        init();
    }

    public DexPatchFile(InputStream is) throws IOException {
        this.buffer = new DexDataBuffer(ByteBuffer.wrap(FileUtils.readStream(is)));
        init();
    }

    private void init() {
        byte[] magic = this.buffer.readByteArray(MAGIC.length);
        if (CompareUtils.uArrCompare(magic, MAGIC) != 0) {
            throw new IllegalStateException("bad dex patch file magic: " + Arrays.toString(magic));
        }

        this.version = this.buffer.readShort();
        if (this.version != VERSION_02 && this.version != VERSION_03) {
            throw new IllegalStateException("bad dex patch file version: " + this.version);
        }

        if (this.version > VERSION_02) {
            this.oldDexAPI = this.buffer.readInt();
            this.patchedDexAPI = this.buffer.readInt();
        }
        this.patchedDexSize = this.buffer.readInt();
        this.firstChunkOffset = this.buffer.readInt();
        this.patchedStringIdSectionOffset = this.buffer.readInt();
        this.patchedTypeIdSectionOffset = this.buffer.readInt();
        this.patchedProtoIdSectionOffset = this.buffer.readInt();
        this.patchedFieldIdSectionOffset = this.buffer.readInt();
        this.patchedMethodIdSectionOffset = this.buffer.readInt();
        if (this.version > DexPatchFile.VERSION_02) {
            this.patchedCallSiteIdSectionOffset = this.buffer.readInt();
            this.patchedMethodHandlesSectionOffset = this.buffer.readInt();
        }
        this.patchedClassDefSectionOffset = this.buffer.readInt();

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Align Tinker versions: use the same release for the gradle plugin (tinker-patch-gradle-plugin) and the app's tinker-android dependency
  2. Upgrade both sides together after checking the supported format matrix
  3. Regenerate the patch with the matching toolchain
Defensive patterns

Strategy: validation

Validate before calling

short v = patchFileVersion; // read header short before full parse
if (v != DexPatchFile.VERSION_02 && v != DexPatchFile.VERSION_03) {
    throw new IOException("unsupported patch format " + v + "; align Tinker versions");
}

Try / catch

try {
    new DexPatchFile(stream);
} catch (IllegalStateException e) {
    if (String.valueOf(e.getMessage()).startsWith("bad dex patch file version")) {
        // version skew: regenerate patch with matching plugin or upgrade loader
    } else { throw e; }
}

Prevention

When it happens

Trigger: Applying a dex patch produced by a Tinker gradle plugin whose DexPatchGenerator writes a different version field than the DexPatchFile reader in the runtime loader accepts.

Common situations: Version skew: patch built with newer Tinker (new format) but app embeds an older tinker-android loader, or vice versa; third-party tools emitting their own patch format versions.

Related errors


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