github/copilot-sdk · error · IndexOutOfBoundsException

Invalid off/len for buffer of length

Error message

Invalid off/len for buffer of length 

What it means

FfiOutputStream.write(byte[], int, int) validates the offset/length pair against the buffer before forwarding bytes to the in-process runtime and throws IndexOutOfBoundsException for any out-of-range off/len. This is the standard OutputStream bounds contract: the sub-range [off, off+len) must lie entirely within b.

Solutions

  1. Clamp off and len to 0..b.length before calling write
  2. Use Arrays.copyOfRange or ByteBuffer to slice the buffer explicitly, then write the slice with off=0
  3. Add an assertion or unit test covering boundary values (off=0, off=b.length, len=0)
  4. Fix the arithmetic that computes remaining bytes (use b.length - off, not b.length - len)

Example fix

// before
out.write(buf, off, buf.length); // ignores off
// after
out.write(buf, off, buf.length - off);
Defensive patterns

Strategy: validation

Validate before calling

if (b == null || off < 0 || len < 0 || off + len > b.length) {
    throw new IllegalArgumentException("bad off/len");
}

Try / catch

try {
    out.write(buf, off, len);
} catch (IndexOutOfBoundsException e) {
    LOG.warning("dropping malformed chunk: " + e.getMessage());
}

Prevention

When it happens

Trigger: Calling write(b, off, len) where off < 0, len < 0, or off + len > b.length — e.g., computing len from a miscounted remaining-bytes variable or passing a null-derived offset.

Common situations: Manual buffer slicing in streaming loops with off-by-one arithmetic; reusing a constant chunk size larger than the actual buffer; passing -1 as len from an EOF sentinel.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/14ee50bd6b073117. Report an issue: GitHub.

Appendix: source

Thrown at java/sdk/src/main/java/com/github/copilot/ffi/FfiOutputStream.java:39

    FfiOutputStream(NativeBinding nativeBinding, AtomicInteger connectionId, AtomicBoolean closing,
            ReentrantLock operationLock) {
        this.nativeBinding = Objects.requireNonNull(nativeBinding, "nativeBinding must not be null");
        this.connectionId = Objects.requireNonNull(connectionId, "connectionId must not be null");
        this.closing = Objects.requireNonNull(closing, "closing must not be null");
        this.operationLock = Objects.requireNonNull(operationLock, "operationLock must not be null");
    }

    @Override
    public void write(int b) throws IOException {
        write(new byte[]{(byte) b}, 0, 1);
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        Objects.requireNonNull(b, "buffer must not be null");
        if (off < 0 || len < 0 || off + len > b.length) {
            throw new IndexOutOfBoundsException("Invalid off/len for buffer of length " + b.length);
        }
        if (len == 0) {
            return;
        }

        operationLock.lock();
        try {
            if (closing.get()) {
                throw new IOException("The in-process runtime connection is closed.");
            }
            int id = connectionId.get();
            if (id == 0) {
                throw new IOException("The in-process runtime connection is closed.");
            }

            byte[] payload = (off == 0 && len == b.length) ? b : Arrays.copyOfRange(b, off, off + len);
            if (!nativeBinding.connectionWrite(id, payload, payload.length)) {
                throw new IOException("Failed to write a frame to the in-process runtime connection.");

View on GitHub (pinned to cd8cf15dc3)