oracle/graal · error · IndexOutOfBoundsException

offset: %s, length: %s, array length: %s

Error message

offset: %s, length: %s, array length: %s

What it means

The native-backed BinaryOutput.write(byte[], int, int) validates (off | len | b.length) < 0 || b.length - off < len and throws IndexOutOfBoundsException('offset: X, length: Y, array length: Z') — the same check as OutputStream.write semantics — before touching native memory. It protects the subsequent native address writes/ByteBuffer put from out-of-range source-array access.

Source

Thrown at compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/truffle/BinaryOutput.java:587

         * Returns an address of an off-heap memory segment containing the marshalled data.
         */
        public CCharPointer getAddress() {
            checkClosed();
            return address;
        }

        @Override
        public void write(int b) {
            checkClosed();
            ensureCapacity(pos + 1);
            address.write(pos++, (byte) b);
        }

        @Override
        public void write(byte[] b, int off, int len) {
            checkClosed();
            if ((off | len | b.length) < 0 || b.length - off < len) {
                throw new IndexOutOfBoundsException("offset: " + off + ", length: " + len + ", array length: " + b.length);
            }
            ensureCapacity(pos + len);
            if (len > BYTEBUFFER_COPY_FROM_ARRAY_THRESHOLD) {
                if (byteBufferView == null) {
                    byteBufferView = CTypeConversion.asByteBuffer(address, length);
                }
                byteBufferView.position(pos);
                byteBufferView.put(b, off, len);
            } else {
                for (int i = 0; i < len; i++) {
                    address.write(pos + i, b[off + i]);
                }
            }
            pos += len;
        }

        @Override
        public void skip(int numberOfBytes) {

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Validate off/len against b.length before the call: Objects.checkFromToIndex(off, off + len, b.length).
  2. Derive len from the array itself when possible (len = b.length - off) instead of a separately tracked variable.
  3. Fix the slicing arithmetic that produced off+len > b.length.
  4. Add a debug assertion of (off, len, b.length) in your wrapper around the native output.

Example fix

// before
nativeOut.write(b, off, len);
// after
Objects.checkFromToIndex(off, off + len, b.length);
nativeOut.write(b, off, len);
Defensive patterns

Strategy: validation

Validate before calling

java.util.Objects.checkFromToIndex(off, off + len, b.length);
nativeOut.write(b, off, len);

Try / catch

try {
    nativeOut.write(b, off, len);
} catch (IndexOutOfBoundsException e) {
    // message contains offset/length/array length: fix slicing at the call site
}

Prevention

When it happens

Trigger: Calling write with negative off or len, off+len exceeding b.length, or a null-array edge producing b.length=0 with len>0 — usually from slicing bugs at the sender of the marshalled data.

Common situations: Substring/subarray slicing where end index exceeds the array; passing len from a protocol field without validating against the actual array size; off-by-one in chunked write loops.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/7d217c2d6527d0e7. Report an issue: GitHub.