oracle/graal · error · IllegalArgumentException

got illegal fromIndex value: %d. fromIndex must be >= 0 and

Error message

got illegal fromIndex value: %d. fromIndex must be >= 0 and <= input length (%d)

What it means

In the 2-argument form (input, fromIndex), RegexExecNode defaults region to the whole string and only validates fromIndex; it must be within [0, inputLength]. A negative start index or one past the end throws IllegalArgumentException.

Source

Thrown at regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/RegexExecNode.java:114

            if (toIndexLong < fromIndex || toIndexLong > regionToLong) {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                throw new IllegalArgumentException(String.format("got illegal toIndex value: %d. toIndex must be >= fromIndex (%d) and <= regionTo (%d)", toIndexLong, fromIndex, regionToLong));
            }
            if (toIndexLong != regionToLong) {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                throw new UnsupportedOperationException(String.format("got non-equal toIndex (%d) and regionTo (%d), support for this is not implemented yet", toIndexLong, regionToLong));
            }
            toIndex = (int) toIndexLong;
            regionFrom = (int) regionFromLong;
            regionTo = (int) regionToLong;
        } else {
            assert args.length == 2;
            toIndex = length;
            regionFrom = 0;
            regionTo = length;
            if (fromIndex < 0 || fromIndex > length) {
                CompilerDirectives.transferToInterpreterAndInvalidate();
                throw new IllegalArgumentException(String.format("got illegal fromIndex value: %d. fromIndex must be >= 0 and <= input length (%d)", fromIndex, length));
            }
        }
        return execute(frame, input, adjustFromIndex(input, (int) fromIndex, regionFrom, regionTo), toIndex, regionFrom, regionTo);
    }

    private int adjustFromIndex(TruffleString input, int fromIndex, int regionFrom, int regionTo) {
        if (mustCheckUTF16Surrogates && fromIndex > regionFrom && fromIndex < regionTo) {
            assert getEncoding().isUTF16();
            if (Character.isLowSurrogate((char) inputRead(input, fromIndex)) && Character.isHighSurrogate((char) inputRead(input, fromIndex - 1))) {
                return fromIndex - 1;
            }
        }
        return fromIndex;
    }

    public final int inputRead(TruffleString input, int i) {
        if (charAtNode == null) {
            CompilerDirectives.transferToInterpreterAndInvalidate();

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Clamp fromIndex: fromIndex = Math.max(0, Math.min(fromIndex, inputLength)).
  2. Initialize and reset lastIndex to 0 for each new input string.
  3. When converting code-point indices to UTF-16 indices, re-check the bound against the actual string length.

Example fix

// before
if (m.find()) idx = m.end(); ... exec(input, idx); // idx may exceed new input's length

// after
idx = Math.max(0, Math.min(idx, input.length()));
exec(input, idx);
Defensive patterns

Strategy: validation

Validate before calling

static boolean validFromIndex2Arg(long fromIndex, int inputLength) {
    return fromIndex >= 0 && fromIndex <= inputLength;
}

Prevention

When it happens

Trigger: Calling the 2-arg regex exec with fromIndex < 0 (e.g. an uninitialized -1 'last match end') or fromIndex > input.length() (e.g. start index carried over from a longer previous input).

Common situations: Search loops that keep a nextIndex variable initialized to -1 and forget to reset it to 0; passing UTF-16 index computed on a differently encoded string whose code-point length exceeds UTF-16 length.

Related errors


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