{"record":{"id":"30fb9bd758ce3046","repo":"apache/dubbo","slug":"invalid-utf-8","errorCode":null,"errorMessage":"invalid UTF-8.","messagePattern":"invalid UTF-8\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"dubbo-common/src/main/java/org/apache/dubbo/common/utils/Utf8Utils.java","lineNumber":88,"sourceCode":"        }\n\n        while (offset < limit) {\n            byte byte1 = srcBytes[offset++];\n            if (DecodeUtil.isOneByte(byte1)) {\n                DecodeUtil.handleOneByteSafe(byte1, destChars, destIdx++);\n                // It's common for there to be multiple ASCII characters in a run mixed in, so add an\n                // extra optimized loop to take care of these runs.\n                while (offset < limit) {\n                    byte b = srcBytes[offset];\n                    if (!DecodeUtil.isOneByte(b)) {\n                        break;\n                    }\n                    offset++;\n                    DecodeUtil.handleOneByteSafe(b, destChars, destIdx++);\n                }\n            } else if (DecodeUtil.isTwoBytes(byte1)) {\n                if (offset >= limit) {\n                    throw new IllegalArgumentException(\"invalid UTF-8.\");\n                }\n                DecodeUtil.handleTwoBytesSafe(byte1, /* byte2 */ srcBytes[offset++], destChars, destIdx++);\n            } else if (DecodeUtil.isThreeBytes(byte1)) {\n                if (offset >= limit - 1) {\n                    throw new IllegalArgumentException(\"invalid UTF-8.\");\n                }\n                DecodeUtil.handleThreeBytesSafe(\n                        byte1,\n                        /* byte2 */ srcBytes[offset++],\n                        /* byte3 */ srcBytes[offset++],\n                        destChars,\n                        destIdx++);\n            } else {\n                if (offset >= limit - 2) {\n                    throw new IllegalArgumentException(\"invalid UTF-8.\");\n                }\n                DecodeUtil.handleFourBytesSafe(\n                        byte1,","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/apache/dubbo/blob/3a3043227f5571d25eb2889de5bca22f2914843b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/Utf8Utils.java#L70-L106","documentation":"Thrown by Utf8Utils.decodeUtf8 when a leading byte signals a 2-byte UTF-8 sequence (0xC2..0xDF) but the buffer ends before the continuation byte arrives (offset>=limit). It is a truncation error: the multi-byte sequence is incomplete at the end of srcSize.","triggerScenarios":"During deserialization, srcSize cuts off exactly after a 2-byte leading byte with no room for its continuation byte. Caused by a truncated payload, an off-by-one in length calculation, or a length prefix that under-counts the bytes of a multi-byte character.","commonSituations":"Network read returned a partial frame. A substring/slice operation split a multi-byte character at the boundary. A serializer wrote a UTF-8 string length in bytes but the reader interpreted it as char count and stopped early. Payload corruption truncating the last character.","solutions":["Confirm srcSize matches the true byte length of the encoded string (re-read the length prefix and the full buffer).","Ensure the source buffer is not truncated; reassemble frames before decoding.","Validate the payload with a standard CharsetDecoder (UTF_8) to locate the truncation point.","If you control the wire format, write byte-length prefixes and read exactly that many bytes before decoding."],"exampleFix":"// before\n// reader consumed asciiLen chars worth then decoded, truncating last multibyte char\nUtf8Utils.decodeUtf8(buf, off, asciiLen, out, 0);\n// after\n// use the exact declared byte length from the stream\nUtf8Utils.decodeUtf8(buf, off, declaredByteLen, out, 0);","handlingStrategy":"validation","validationCode":"boolean isCompleteUtf8(byte[] b, int off, int len) {\n    int end = off + len, i = off;\n    while (i < end) {\n        int c = b[i] & 0xFF;\n        int need;\n        if (c < 0x80) need = 0;\n        else if ((c >> 5) == 0x6) need = 1;\n        else if ((c >> 4) == 0xE) need = 2;\n        else if ((c >> 3) == 0x1E) need = 3;\n        else return false;\n        if (i + need >= end) return false; // truncated\n        i += 1 + need;\n    }\n    return true;\n}","typeGuard":null,"tryCatchPattern":"try {\n    Utf8Utils.decodeUtf8(src, off, len, out, 0);\n} catch (IllegalArgumentException e) {\n    if (\"invalid UTF-8.\".equals(e.getMessage())) { /* truncated/invalid — re-read frame */ }\n    throw e;\n}","preventionTips":["Buffer complete frames before UTF-8 decoding.","Use byte-accurate length prefixes on the wire, never char counts.","Validate payload completeness with a UTF-8 CharsetDecoder in tests."],"tags":["utf8","serialization","truncation","deserialization"],"backgroundTag":null,"analyzedSha":"3a3043227f5571d25eb2889de5bca22f2914843b","analyzedAt":"2026-08-14T00:43:19.853Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}