{"record":{"id":"9302147267329517","repo":"Tencent/tinker","slug":"bad-second-or-third-byte","errorCode":null,"errorMessage":"bad second or third byte","messagePattern":"bad second or third byte","errorType":"exception","errorClass":"UTFDataFormatException","httpStatus":null,"severity":"error","filePath":"third-party/aosp-dexutils/src/main/java/com/tencent/tinker/android/dex/Mutf8.java","lineNumber":54,"sourceCode":"        while (true) {\n            char a = (char) (in.readByte() & 0xff);\n            if (a == 0) {\n                return new String(out, 0, s);\n            }\n            out[s] = a;\n            if (a < '\\u0080') {\n                s++;\n            } else if ((a & 0xe0) == 0xc0) {\n                int b = in.readByte() & 0xff;\n                if ((b & 0xC0) != 0x80) {\n                    throw new UTFDataFormatException(\"bad second byte\");\n                }\n                out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F));\n            } else if ((a & 0xf0) == 0xe0) {\n                int b = in.readByte() & 0xff;\n                int c = in.readByte() & 0xff;\n                if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {\n                    throw new UTFDataFormatException(\"bad second or third byte\");\n                }\n                out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));\n            } else {\n                throw new UTFDataFormatException(\"bad byte\");\n            }\n        }\n    }\n\n    /**\n     * Returns the number of bytes the modified UTF8 representation of 's' would take.\n     */\n    public static long countBytes(String s, boolean shortLength) throws UTFDataFormatException {\n        long result = 0;\n        final int length = s.length();\n        for (int i = 0; i < length; ++i) {\n            char ch = s.charAt(i);\n            if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.\n                ++result;","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/third-party/aosp-dexutils/src/main/java/com/tencent/tinker/android/dex/Mutf8.java#L36-L72","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"catch (UTFDataFormatException e) -> fail the whole dex with its identity and the failing string id; partial decode yields silently wrong identifiers downstream","preventionTips":["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."],"tags":["dex","mutf8","string-decoding","parsing","tinker"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}