apache/dubbo · error · IllegalArgumentException

invalid UTF-8.

Error message

invalid UTF-8.

What it means

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.

Source

Thrown at dubbo-common/src/main/java/org/apache/dubbo/common/utils/Utf8Utils.java:88

        }

        while (offset < limit) {
            byte byte1 = srcBytes[offset++];
            if (DecodeUtil.isOneByte(byte1)) {
                DecodeUtil.handleOneByteSafe(byte1, destChars, destIdx++);
                // It's common for there to be multiple ASCII characters in a run mixed in, so add an
                // extra optimized loop to take care of these runs.
                while (offset < limit) {
                    byte b = srcBytes[offset];
                    if (!DecodeUtil.isOneByte(b)) {
                        break;
                    }
                    offset++;
                    DecodeUtil.handleOneByteSafe(b, destChars, destIdx++);
                }
            } else if (DecodeUtil.isTwoBytes(byte1)) {
                if (offset >= limit) {
                    throw new IllegalArgumentException("invalid UTF-8.");
                }
                DecodeUtil.handleTwoBytesSafe(byte1, /* byte2 */ srcBytes[offset++], destChars, destIdx++);
            } else if (DecodeUtil.isThreeBytes(byte1)) {
                if (offset >= limit - 1) {
                    throw new IllegalArgumentException("invalid UTF-8.");
                }
                DecodeUtil.handleThreeBytesSafe(
                        byte1,
                        /* byte2 */ srcBytes[offset++],
                        /* byte3 */ srcBytes[offset++],
                        destChars,
                        destIdx++);
            } else {
                if (offset >= limit - 2) {
                    throw new IllegalArgumentException("invalid UTF-8.");
                }
                DecodeUtil.handleFourBytesSafe(
                        byte1,

View on GitHub (pinned to 3a3043227f)

Solutions

  1. Confirm srcSize matches the true byte length of the encoded string (re-read the length prefix and the full buffer).
  2. Ensure the source buffer is not truncated; reassemble frames before decoding.
  3. Validate the payload with a standard CharsetDecoder (UTF_8) to locate the truncation point.
  4. If you control the wire format, write byte-length prefixes and read exactly that many bytes before decoding.

Example fix

// before
// reader consumed asciiLen chars worth then decoded, truncating last multibyte char
Utf8Utils.decodeUtf8(buf, off, asciiLen, out, 0);
// after
// use the exact declared byte length from the stream
Utf8Utils.decodeUtf8(buf, off, declaredByteLen, out, 0);
Defensive patterns

Strategy: validation

Validate before calling

boolean isCompleteUtf8(byte[] b, int off, int len) {
    int end = off + len, i = off;
    while (i < end) {
        int c = b[i] & 0xFF;
        int need;
        if (c < 0x80) need = 0;
        else if ((c >> 5) == 0x6) need = 1;
        else if ((c >> 4) == 0xE) need = 2;
        else if ((c >> 3) == 0x1E) need = 3;
        else return false;
        if (i + need >= end) return false; // truncated
        i += 1 + need;
    }
    return true;
}

Try / catch

try {
    Utf8Utils.decodeUtf8(src, off, len, out, 0);
} catch (IllegalArgumentException e) {
    if ("invalid UTF-8.".equals(e.getMessage())) { /* truncated/invalid — re-read frame */ }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14). Data as JSON: /api/errors/30fb9bd758ce3046. Report an issue: GitHub.