Tencent/tinker · error · UTFDataFormatException

bad byte

Error message

bad byte

What it means

Thrown by Mutf8.decode when the lead byte of a character is not < 0x80, not 0b110xxxxx, and not 0b1110xxxx — i.e. a bare continuation byte (0x80–0xBF) or an invalid 0xF0–0xFF byte where a lead byte was expected. MUTF-8 allows only 1-, 2-, and 3-byte forms.

Source

Thrown at third-party/aosp-dexutils/src/main/java/com/tencent/tinker/android/dex/Mutf8.java:58

            }
            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;
            } else if (ch <= 2047) {
                result += 2;
            } else {
                result += 3;

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. If you generate the dex, ensure supplementary characters (emoji, rare CJK) are written as surrogate pairs via Mutf8.encode, not as 4-byte UTF-8.
  2. Re-align: recompute the string_data_item offset from the string_ids table before decoding.
  3. If a length prefix (utf16_size) mismatch is possible, decode with an explicit length bound rather than until NUL.
Defensive patterns

Strategy: try-catch

Try / catch

catch (UTFDataFormatException e) -> quarantine the dex, log first 16 bytes at the failing offset for triage

Prevention

When it happens

Trigger: The decode position lands mid-character (previous sequence consumed the wrong number of bytes), or the data contains raw 4-byte UTF-8 (standard Java UTF-8, not MUTF-8) or binary garbage in the string region.

Common situations: Strings written with standard UTF-8 (4-byte surrogates for supplementary chars) instead of MUTF-8's CESU-8-style surrogate pairs; buffer underflow reading past the string's declared length into unrelated bytes.

Related errors


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