oracle/graal · error · IllegalArgumentException

Invalid output range: {}..{} for array of length {}

Error message

Invalid output range: {}..{} for array of length {}

What it means

After validating the source range, copyMemory checks that the destination byte[] can hold bytesToCopy = srcTo - srcFrom starting at dstFrom. This IllegalArgumentException fires when dstFrom is negative or dstFrom + bytesToCopy would run past dst.length — the destination slice is too small for the requested bytes.

Source

Thrown at compiler/src/jdk.graal.compiler.hostvmaccess/src/jdk/graal/compiler/hostvmaccess/HostVMAccess.java:499

    @Override
    public void copyMemory(JavaConstant src, int srcFrom, int srcTo, byte[] dst, int dstFrom) {
        ResolvedJavaType arrayType = getProviders().getMetaAccess().lookupJavaType(src);
        if (arrayType == null || !arrayType.isArray() || !arrayType.getComponentType().isPrimitive()) {
            throw new IllegalArgumentException("Expected a primitive array constant, got " + src);
        }
        var array = providers.getSnippetReflection().asObject(Object.class, src);
        if (array == null) {
            throw new IllegalArgumentException("Could not unwrap an array constant: " + src);
        }
        int sourceArrayEnd = Array.getLength(array) * arrayType.getComponentType().getJavaKind().getByteCount();
        if (srcFrom < 0 || srcTo > sourceArrayEnd || srcTo < srcFrom) {
            throw new IllegalArgumentException(
                            "Invalid input range: " + srcFrom + ".." + srcTo + " for array of length " + Array.getLength(array) + " with kind " + arrayType.getComponentType().getJavaKind());
        }
        int bytesToCopy = srcTo - srcFrom;
        if (dstFrom < 0 || dstFrom > dst.length - bytesToCopy) {
            throw new IllegalArgumentException("Invalid output range: " + dstFrom + ".." + (dstFrom + bytesToCopy) + " for array of length " + dst.length);
        }
        var unsafe = Unsafe.getUnsafe();
        unsafe.copyMemory(array, unsafe.arrayBaseOffset(array.getClass()) + srcFrom, dst, Unsafe.ARRAY_BYTE_BASE_OFFSET + dstFrom, bytesToCopy);
    }

    /**
     * Host mode performs the unaligned read with {@link Unsafe} against the unwrapped hosted array
     * object.
     */
    @Override
    public JavaConstant readPrimitiveArrayUnaligned(JavaConstant primitiveArray, JavaKind kind, int offset) {
        if (kind == null || !kind.isPrimitive() || kind == JavaKind.Void) {
            throw new IllegalArgumentException("Expected a non-void primitive kind, got " + kind);
        }
        ResolvedJavaType arrayType = getProviders().getMetaAccess().lookupJavaType(primitiveArray);
        if (arrayType == null || !arrayType.isArray() || !arrayType.getComponentType().isPrimitive()) {
            throw new IllegalArgumentException("Expected a primitive array constant, got " + primitiveArray);
        }

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Allocate dst with at least (srcTo - srcFrom) bytes and use dstFrom = 0, or verify dst.length - dstFrom >= srcTo - srcFrom before the call
  2. Derive the buffer size from the component kind's byte count, not the element count
  3. Write a small guard that clamps or rejects the copy when the destination is too small

Example fix

// before
byte[] dst = new byte[4]; // but src is long[1] => 8 bytes
vmAccess.copyMemory(srcConst, 0, 8, dst, 0);

// after
int bytes = srcTo - srcFrom;
byte[] dst = new byte[bytes];
vmAccess.copyMemory(srcConst, srcFrom, srcTo, dst, 0);
Defensive patterns

Strategy: validation

Validate before calling

int bytes = srcTo - srcFrom;
if (dst == null || dstFrom < 0 || dst.length - dstFrom < bytes) {
    dst = new byte[bytes]; dstFrom = 0; // or reject
}

Prevention

When it happens

Trigger: Calling copyMemory with a dst byte[] shorter than srcTo - srcFrom, or a dstFrom that leaves insufficient room: dst = new byte[4] while copying 8 bytes from a long[]; or dstFrom near the end of a larger buffer.

Common situations: Sizing the output buffer from element count instead of byte count; reusing a fixed-size scratch buffer across arrays of different kinds; forgetting that dstFrom shifts the window.

Related errors


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