Tencent/tinker · error · UTFDataFormatException
bad second or third byte
Error message
bad second or third byte
What it means
Thrown by Mutf8.decode when a 3-byte MUTF-8 sequence (lead byte 0xE0–0xEF) has a second or third byte that is not a 0b10xxxxxx continuation. All non-ASCII chars above U+07FF occupy 3 bytes in MUTF-8 and both trailing bytes must be continuations.
Source
Thrown at third-party/aosp-dexutils/src/main/java/com/tencent/tinker/android/dex/Mutf8.java:54
while (true) {
char a = (char) (in.readByte() & 0xff);
if (a == 0) {
return new String(out, 0, s);
}
out[s] = a;
if (a < '\u0080') {
s++;
} else if ((a & 0xe0) == 0xc0) {
int b = in.readByte() & 0xff;
if ((b & 0xC0) != 0x80) {
throw new UTFDataFormatException("bad second byte");
}
out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F));
} else if ((a & 0xf0) == 0xe0) {
int b = in.readByte() & 0xff;
int c = in.readByte() & 0xff;
if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {
throw new UTFDataFormatException("bad second or third byte");
}
out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));
} else {
throw new UTFDataFormatException("bad byte");
}
}
}
/**
* Returns the number of bytes the modified UTF8 representation of 's' would take.
*/
public static long countBytes(String s, boolean shortLength) throws UTFDataFormatException {
long result = 0;
final int length = s.length();
for (int i = 0; i < length; ++i) {
char ch = s.charAt(i);
if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
++result;View on GitHub (pinned to 1b7ea02c23)
Solutions
- Verify the utf16_size prefix handling just before the string bytes — reading size with the wrong varint width shifts the whole decode.
- Checksum-validate the dex; corrupt 3-byte sequences almost always mean a damaged file.
- Reprocess the dex through a spec-conformant toolchain (d8/dx) and retry.
Defensive patterns
Strategy: try-catch
Try / catch
catch (UTFDataFormatException e) -> fail the whole dex with its identity and the failing string id; partial decode yields silently wrong identifiers downstream
Prevention
- When reading string_data_item always honor the utf16_size prefix exactly; do not decode to NUL as a substitute on bounded fields.
- Avoid obfuscators that re-encrypt the string pool in place.
When it happens
Trigger: Decoding CJK/emoji/supplementary-string data whose continuation bytes are corrupt, or an off-by-one/two buffer position landing mid-sequence (lead byte read as continuation).
Common situations: Same family as the 2-byte failure: corrupt dex string sections, misencrypted string pools from obfuscators, or readers positioned incorrectly after a length prefix (utf16_size) mismatch.
Related errors
- bad second byte
- bad byte
- Declared length ${expectedLength} doesn't match decoded leng
- Unexpected type: ${type}
- invalid LEB128 sequence
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/9302147267329517.
Report an issue: GitHub.