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
- 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.
- Verify the patch file's MD5/SHA against the value from the server before calling BSPatch; re-download on mismatch.
- Regenerate the patch from the exact old/new pair with the same bsdiff/bspatch implementation pair.
- 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
- Ship and check MD5s for both the base file and the patch before apply.
- Download patches to a temp file, verify size and hash, then atomically rename.
- Never re-sign or re-zipalign the base APK after the patch is generated — bytes must stay identical.
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
- patch %s extract failed (%s).
- patch %s extract failed (%s).
- patch dex file md5 is mismatch, but path is null!!!!
- resource file md5 mismatch, but patch resource file not foun
- Could not create new AssetManager
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/cbd9b35ab49719fe.
Report an issue: GitHub.