pxb1988/dex2jar · error · UTFDataFormatException

bad second byte

Error message

bad second byte

What it means

Thrown by Mutf8.decode when decoding Modified UTF-8: a lead byte indicated a 2-byte sequence (0xC0-0xDF), but the next byte read from the ByteBuffer is not a continuation byte (top bits not 10). The input byte stream is not valid Modified UTF-8, so decoding aborts with UTFDataFormatException.

Solutions

  1. Verify the string offset is correct: re-parse string_ids_off and ensure the decoder starts at the ULEB128 length prefix, not inside the data
  2. Check the DEX file is not truncated or corrupted; re-obtain or rebuild it with dx/d8
  3. Wrap decode in try-catch for UTFDataFormatException and treat the file as invalid rather than crashing

Example fix

// before
String s = Mutf8.decode(buffer, new int[1]); // crashes on corrupt data
// after
int[] pos = new int[1];
try {
    String s = Mutf8.decode(buffer, pos);
} catch (UTFDataFormatException e) {
    throw new IllegalStateException("corrupt DEX string at " + pos[0], e);
}
Defensive patterns

Strategy: validation

Validate before calling

static boolean looksLikeMutf8(java.nio.ByteBuffer in, int start) {
    for (int i = start; i < in.limit(); i++) {
        int a = in.get(i) & 0xff;
        if (a >= 0xF0) return false;
        if ((a & 0xE0) == 0xC0 || (a & 0xF0) == 0xE0) {
            if (i + 1 >= in.limit() || ((in.get(i + 1) & 0xC0) != 0x80)) return false;
            i += 1;
        }
    }
    return true;
}

Type guard

boolean isValidMutf8Lead(int a) { return (a & 0x80) == 0 || (a & 0xE0) == 0xC0 || (a & 0xF0) == 0xE0; }

Try / catch

try { String s = Mutf8.decode(buf, pos); } catch (UTFDataFormatException e) { log.warn("bad MUTF-8 at " + pos[0]); return null; }

Prevention

When it happens

Trigger: Decoding a DEX string_data item (or other MUTF-8 blob) whose second byte of a 2-byte sequence is corrupted, misaligned, or the decoder is pointed at the wrong offset.

Common situations: Corrupted or hand-edited DEX files, wrong string offsets after incorrect parsing of string_ids/data offsets, truncated buffers where read position drifts into non-UTF data.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of pxb1988/dex2jar@b5bda4fb49 (2026-09-08). Data as JSON: /api/errors/9130e6dbe468e377. Report an issue: GitHub.

Appendix: source

Thrown at dex-reader/src/main/java/com/googlecode/d2j/util/Mutf8.java:48

    }

    /**
     * Decodes bytes from {@code in} into {@code sb} until a delimiter 0x00 is encountered. Returns a new string
     * containing the decoded characters.
     */
    public static String decode(ByteBuffer in, StringBuilder sb) throws UTFDataFormatException {
        while (true) {
            char a = (char) (in.get() & 0xff);
            if (a == 0) {
                return sb.toString();
            }

            if (a < '\u0080') {
                sb.append(a);
            } else if ((a & 0xe0) == 0xc0) {
                int b = in.get() & 0xff;
                if ((b & 0xC0) != 0x80) {
                    throw new UTFDataFormatException("bad second byte");
                }
                sb.append((char) (((a & 0x1F) << 6) | (b & 0x3F)));
            } else if ((a & 0xf0) == 0xe0) {
                int b = in.get() & 0xff;
                int c = in.get() & 0xff;
                if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {
                    throw new UTFDataFormatException("bad second or third byte");
                }
                sb.append((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.
     */

View on GitHub (pinned to b5bda4fb49)