Tencent/tinker · critical · IOException

Corrupt by wrong patch file.

Error message

Corrupt by wrong patch file.

What it means

BSPatch.apply streams control triples (add-length, copy-length, seek) and applies them to produce the new file. The first control value ctrl[0] says how many bytes the diff block contributes at the current new-file position; if newpos + ctrl[0] would overrun newsize, the patch's control stream disagrees with the declared output size, so the patch cannot be applied to this input. This is a hard integrity check that prevents writing past the end of the output buffer.

Source

Thrown at third-party/bsdiff-util/src/main/java/com/tencent/tinker/bsdiff/BSPatch.java:347

        in.skip(diffBlockLen + ctrlBlockLen + BSUtil.HEADER_SIZE);
        InputStream extraBlockIn = new GZIPInputStream(in);

        // byte[] newBuf = new byte[newsize + 1];
        byte[] newBuf = new byte[newsize];

        int oldpos = 0;
        int newpos = 0;
        int[] ctrl = new int[3];

        // int nbytes;
        while (newpos < newsize) {

            for (int i = 0; i <= 2; i++) {
                ctrl[i] = ctrlBlockIn.readInt();
            }

            if (newpos + ctrl[0] > newsize) {
                throw new IOException("Corrupt by wrong patch file.");
            }

            // Read ctrl[0] bytes from diffBlock stream
            if (!BSUtil.readFromStream(diffBlockIn, newBuf, newpos, ctrl[0])) {
                throw new IOException("Corrupt by wrong patch file.");
            }

            for (int i = 0; i < ctrl[0]; i++) {
                if ((oldpos + i >= 0) && (oldpos + i < oldsize)) {
                    newBuf[newpos + i] += oldBuf[oldpos + i];
                }
            }

            newpos += ctrl[0];
            oldpos += ctrl[0];

            if (newpos + ctrl[1] > newsize) {
                throw new IOException("Corrupt by wrong patch file.");

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Verify the old file's digest matches the digest recorded when the patch was created (tinker patches carry old-file MD5s); if not, fetch or locate the correct base version.
  2. Verify the patch file's MD5/SHA against the value from the server before calling BSPatch; re-download on mismatch.
  3. Regenerate the patch from the exact old/new pair with the same bsdiff/bspatch implementation pair.
  4. If corruption recurs, check storage/transport: disk full, interrupted download, or a proxy mangling binary payloads.
Defensive patterns

Strategy: validation

Validate before calling

// Verify old-file and patch digests before applying a bsdiff patch
boolean safeToPatch(java.io.File oldFile, java.io.File patchFile,
                    String expectedOldMd5, String expectedPatchMd5) throws Exception {
    return md5Of(oldFile).equals(expectedOldMd5)
        && md5Of(patchFile).equals(expectedPatchMd5);
}
String md5Of(java.io.File f) throws Exception {
    java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
    try (java.io.InputStream in = new java.io.FileInputStream(f)) {
        byte[] buf = new byte[8192]; int n;
        while ((n = in.read(buf)) > 0) md.update(buf, 0, n);
    }
    StringBuilder sb = new StringBuilder();
    for (byte b : md.digest()) sb.append(String.format("%02x", b));
    return sb.toString();
}

Try / catch

try {
    byte[] out = com.tencent.tinker.bsdiff.BSPatch.patch(oldData, newDataLen, patchStream);
} catch (java.io.IOException e) {
    if ("Corrupt by wrong patch file.".equals(e.getMessage())) {
        // do NOT retry with the same inputs; re-verify digests, re-download patch or correct base file
        throw new IllegalStateException("patch/base mismatch: verify old-file and patch MD5s", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling BSPatch.patch (or its variants) where the control triple's first entry would push newpos beyond newsize: a patch file truncated or byte-corrupted in its control block, a patch applied to an old input that is not the exact base it was diffed against, or a patch generated by an incompatible bsdiff version/format.

Common situations: OTA/patch applied to the wrong base APK version; patch file corrupted during download (partial transfer, proxy truncation); MD5 of the patch not verified before apply; mixing bsdiff/bspatch builds with different control-block endianness or header layouts.

Related errors


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