netty/netty · error · IndexOutOfBoundsException

%s: %d, length: %d (expected: range(0, %d))

Error message

%s: %d, length: %d (expected: range(0, %d))

What it means

Thrown by AbstractByteBuf.rangeBoundsCheckFailed via checkRangeBounds/isOutOfBounds (an IndexOutOfBoundsException). This is the generic workhorse for primitive and indexed get/set operations (e.g. checkIndex0, checkSrcType). isOutOfBounds is true when index < 0, fieldLength < 0, index + fieldLength < 0 (overflow), or index + fieldLength > capacity. indexName is usually 'index', so the message reads e.g. 'index: 5, length: 10 (expected: range(0, 100))'.

Source

Thrown at buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java:1417

    }

    /**
     * This is a simplified version of MathUtil.isOutOfBounds that does not check for capacity negative values.
     */
    private static boolean isOutOfBoundsTrustedCapacity(int index, int fieldLength, int capacity) {
        // keep these as branches since would make it easier to be constant-folded
        return index < 0 || fieldLength < 0 || index + fieldLength < 0 || index + fieldLength > capacity;
    }

    private static void checkRangeBounds(final String indexName, final int index,
            final int fieldLength, final int capacity) {
        if (isOutOfBounds(index, fieldLength, capacity)) {
            rangeBoundsCheckFailed(indexName, index, fieldLength, capacity);
        }
    }

    private static void rangeBoundsCheckFailed(String indexName, int index, int fieldLength, int capacity) {
        throw new IndexOutOfBoundsException(String.format(
                "%s: %d, length: %d (expected: range(0, %d))", indexName, index, fieldLength, capacity));
    }

    final void checkIndex0(int index, int fieldLength) {
        if (checkBounds) {
            checkRangeBoundsTrustedCapacity("index", index, fieldLength, capacity());
        }
    }

    protected final void checkSrcIndex(int index, int length, int srcIndex, int srcCapacity) {
        checkIndex(index, length);
        if (checkBounds) {
            checkRangeBounds("srcIndex", srcIndex, length, srcCapacity);
        }
    }

    protected final void checkDstIndex(int index, int length, int dstIndex, int dstCapacity) {
        checkIndex(index, length);

View on GitHub (pinned to 70040aacae)

Solutions

  1. Validate against the correct bound: use readableBytes() for read-side offsets (index is relative to readerIndex) and capacity() for absolute set operations.
  2. Sanitize parsed lengths: if (len < 0 || (long) idx + len > buf.capacity()) throw your own clearer error.
  3. For derived offsets, prefer the read-flavored API (readInt/readBytes) which advances the cursor and bounds-checks against readable bytes automatically.

Example fix

// before
buf.setInt(offset + 4, value); // offset + 4 + 4 > buf.capacity()

// after
int idx = offset + 4;
if (idx < 0 || (long) idx + 4 > buf.capacity()) {
    throw new DecoderException("header offset out of range: " + idx);
}
buf.setInt(idx, value);
Defensive patterns

Strategy: validation

Validate before calling

// Validate absolute range before indexed get/set
static void ensureRange(ByteBuf buf, int index, int len) {
    if (index < 0 || len < 0 || (long) index + len > buf.capacity()) {
        throw new IllegalArgumentException("bad range");
    }
}
ensureRange(buf, idx, 4);
buf.setInt(idx, value);

Type guard

static boolean inRange(ByteBuf buf, int index, int len) {
    return index >= 0 && len >= 0 && (long) index + len <= buf.capacity();
}

Try / catch

try {
    return buf.getInt(idx);
} catch (IndexOutOfBoundsException e) {
    throw new DecoderException("field offset " + idx + " out of range", e);
}

Prevention

When it happens

Trigger: Calling getInt/setInt/getBytes(index,...)/setBytes(index,...) and similar indexed accessors with a negative index, a negative length, an index+length that overflows int, or an index+length past capacity; reading a field at an offset computed from an attacker-controlled framing length.

Common situations: Decoding variable-length fields where the declared offset/length was not validated against the buffer; off-by-one in offset arithmetic (using capacity vs readableBytes); signed-length bug where a parsed length is negative.

Related errors


AI-assisted analysis of netty/netty@70040aacae (2026-08-14). Data as JSON: /api/errors/952b02501a9f1bf3. Report an issue: GitHub.