apache/dubbo · error · ArrayIndexOutOfBoundsException

buffer srcBytes.length=%d, srcIdx=%d, srcSize=%d, destChars.

Error message

buffer srcBytes.length=%d, srcIdx=%d, srcSize=%d, destChars.length=%d, destIdx=%d

What it means

Thrown by Utf8Utils.decodeUtf8 as an ArrayIndexOutOfBoundsException when the source/destination buffer bounds are violated: srcIdx or srcSize is negative, srcIdx+srcSize exceeds srcBytes.length, or destIdx+srcSize exceeds destChars.length. This is a pre-flight bounds guard before any byte is decoded, so it signals a caller arithmetic error, not bad data.

Source

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

/**
 * See original <a href=
 * "https://github.com/protocolbuffers/protobuf/blob/master/java/core/src/main/java/com/google/protobuf/Utf8.java"
 * >Utf8.java</a>
 */
public final class Utf8Utils {

    private Utf8Utils() {
        //empty
    }

    public static int decodeUtf8(byte[] srcBytes, int srcIdx, int srcSize, char[] destChars, int destIdx) {
        // Bitwise OR combines the sign bits so any negative value fails the check.
        if ((srcIdx | srcSize | srcBytes.length - srcIdx - srcSize) < 0
                || (destIdx | destChars.length - destIdx - srcSize) < 0) {
            String exMsg = String.format("buffer srcBytes.length=%d, srcIdx=%d, srcSize=%d, destChars.length=%d, " +
                    "destIdx=%d", srcBytes.length, srcIdx, srcSize, destChars.length, destIdx);
            throw new ArrayIndexOutOfBoundsException(
                    exMsg);
        }

        int offset = srcIdx;
        final int limit = offset + srcSize;
        final int destIdx0 = destIdx;

        // Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this).
        // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
        while (offset < limit) {
            byte b = srcBytes[offset];
            if (!DecodeUtil.isOneByte(b)) {
                break;
            }
            offset++;
            DecodeUtil.handleOneByteSafe(b, destChars, destIdx++);
        }

View on GitHub (pinned to 3a3043227f)

Solutions

  1. Verify srcIdx>=0, srcSize>=0, and srcIdx+srcSize<=srcBytes.length before calling decodeUtf8.
  2. Ensure destChars is allocated with at least srcSize chars (worst case 1 char per byte for the safe variant) and destIdx+srcSize<=destChars.length.
  3. If the inputs come from a length-prefixed stream, validate the length against the remaining buffer before decoding.
  4. Check for serialization protocol version mismatch between producer and consumer if the length prefix looks wrong.

Example fix

// before
char[] out = new char[asciiLen]; // too small if non-ascii
Utf8Utils.decodeUtf8(bytes, 0, bytes.length, out, 0);
// after
char[] out = new char[bytes.length]; // safe upper bound
if (0 > 0 || bytes.length > bytes.length || bytes.length > out.length) {
    throw new IllegalArgumentException("bad bounds");
}
Utf8Utils.decodeUtf8(bytes, 0, bytes.length, out, 0);
Defensive patterns

Strategy: validation

Validate before calling

void checkBounds(byte[] src, int srcIdx, int srcSize, char[] dest, int destIdx) {
    if (srcIdx < 0 || srcSize < 0 || srcIdx + srcSize > src.length
            || destIdx < 0 || destIdx + srcSize > dest.length) {
        throw new IllegalArgumentException("decodeUtf8 bounds invalid");
    }
}
// call before Utf8Utils.decodeUtf8(...)

Try / catch

try {
    Utf8Utils.decodeUtf8(src, srcIdx, srcSize, dest, destIdx);
} catch (ArrayIndexOutOfBoundsException e) {
    // bounds mismatch — recompute srcSize/dest allocation, do not retry blindly
    throw new IllegalArgumentException("bad utf8 buffer bounds", e);
}

Prevention

When it happens

Trigger: decodeUtf8(srcBytes, srcIdx, srcSize, destChars, destIdx) is called with srcIdx<0, srcSize<0, srcIdx+srcSize>srcBytes.length, or destIdx+srcSize>destChars.length. This happens in Dubbo's serialization/deserialization path when a length prefix is corrupted or a destination buffer was sized too small for the declared UTF-8 byte count.

Common situations: Corrupt or truncated network payload where the declared length exceeds the actual buffer. A deserializer computed destChars length assuming all-ASCII (1 byte/char) but the data contains multi-byte sequences needing the same char count yet a mis-sized array. Version mismatch between serializer and deserializer producing wrong length prefixes. Manually slicing a byte array with wrong offsets.

Related errors


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