antlr/antlr4 · error · IllegalArgumentException

cannot seek to negative index {index}

Error message

cannot seek to negative index {index}

What it means

UnbufferedCharStream.seek(index) computes the offset of index from the buffer start; a negative offset means the requested character position lies before the retained window, which an unbuffered stream cannot re-read. It throws IllegalArgumentException ('cannot seek to negative index') rather than silently re-reading discarded data.

Source

Thrown at runtime/Java/src/org/antlr/v4/runtime/UnbufferedCharStream.java:291

	/** Seek to absolute character index, which might not be in the current
	 *  sliding window.  Move {@code p} to {@code index-bufferStartIndex}.
	 */
    @Override
    public void seek(int index) {
		if (index == currentCharIndex) {
			return;
		}

		if (index > currentCharIndex) {
			sync(index - currentCharIndex);
			index = Math.min(index, getBufferStartIndex() + n - 1);
		}

        // index == to bufferStartIndex should set p to 0
        int i = index - getBufferStartIndex();
        if ( i < 0 ) {
			throw new IllegalArgumentException("cannot seek to negative index " + index);
		}
		else if (i >= n) {
            throw new UnsupportedOperationException("seek to index outside buffer: "+
                    index+" not in "+getBufferStartIndex()+".."+(getBufferStartIndex()+n));
        }

		p = i;
		currentCharIndex = index;
		if (p == 0) {
			lastChar = lastCharBufferStart;
		}
		else {
			lastChar = data[p-1];
		}
    }

    @Override
    public int size() {

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Hold a mark() across the region you may need to re-seek into, and release it only when done
  2. Extract the text you will need later (via getText) before the buffer moves past it
  3. Switch to a buffered stream (ANTLRInputStream / fromReader with buffering) if arbitrary backward seeks are required

Example fix

// before
// ... lex far ahead ...
stream.seek(0); // IllegalArgumentException: 0 < bufferStartIndex

// after
int m = stream.mark();
try {
    // ... lex, then rewind safely ...
    stream.seek(stream.index() ); // stay within marked window
} finally {
    stream.release(m);
}
Defensive patterns

Strategy: validation

Validate before calling

// bufferStartIndex == index() - (buffered count); track a floor via marks
int floor = markedFloorIndex; // recorded at mark() time
if (index < floor) {
    throw new UnsupportedOperationException("target " + index + " was discarded; use a buffered stream");
}
stream.seek(index);

Try / catch

try { stream.seek(target); }
catch (IllegalArgumentException | UnsupportedOperationException e) { /* re-open input in a buffered stream and retry the whole parse */ }

Prevention

When it happens

Trigger: seek(0) or any backward seek after the buffer has advanced past the target (markers released, earlier chars discarded); seek to an index below the current bufferStartIndex after long forward consumption.

Common situations: Error reporting or token-highlighting code that rewinds to re-extract text after parsing has moved on; generic stream utilities that assume seek(0) is always legal; migrating code from ANTLRInputStream (buffered) to UnbufferedCharStream for memory reasons.

Related errors


AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14). Data as JSON: /api/errors/339c11501dfab319. Report an issue: GitHub.