antlr/antlr4 · error · UnsupportedOperationException

seek to index outside buffer: {index} not in {bufferStartInd

Error message

seek to index outside buffer: {index} not in {bufferStartIndex}..{bufferStartIndex+n}

What it means

After syncing and clamping a forward seek, UnbufferedTokenStream verifies that the resulting relative position is still inside bufferStartIndex..bufferStartIndex+n. If it is not, the stream cannot honor the seek without retaining tokens, so it throws UnsupportedOperationException. In normal operation forward seeks are clamped to EOF, so this guard also catches inconsistent state or unsupported random access.

Source

Thrown at runtime/Java/src/org/antlr/v4/runtime/UnbufferedTokenStream.java:259

	@Override
	public void seek(int index) { // seek to absolute index
		if (index == currentTokenIndex) {
			return;
		}

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

		int bufferStartIndex = getBufferStartIndex();
		int i = index - bufferStartIndex;
		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 "+ bufferStartIndex +".."+(bufferStartIndex +n));
		}

		p = i;
		currentTokenIndex = index;
		if (p == 0) {
			lastToken = lastTokenBufferStart;
		}
		else {
			lastToken = tokens[p-1];
		}
	}

	@Override
	public int size() {
		throw new UnsupportedOperationException("Unbuffered stream cannot know its size");
	}

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Use BufferedTokenStream or CommonTokenStream for random-access seeks.
  2. Keep a marker active for the whole region in which seeks are allowed on an unbuffered stream.
  3. Seek only to indexes already represented in the buffer and never after release of the protecting mark.
  4. If subclassing UnbufferedTokenStream, preserve its p, n, and currentTokenIndex invariants.

Example fix

// before
UnbufferedTokenStream<Token> tokens = new UnbufferedTokenStream<>(lexer);
tokens.seek(requestedTokenIndex); // may fall outside retained window

// after
CommonTokenStream tokens = new CommonTokenStream(lexer);
tokens.seek(requestedTokenIndex); // buffered until EOF
Defensive patterns

Strategy: try-catch

Type guard

static boolean supportsRandomAccess(TokenStream tokens) {
    return tokens instanceof BufferedTokenStream;
}

Try / catch

try {
    tokens.seek(index);
} catch (UnsupportedOperationException e) {
    throw new IllegalArgumentException("Random access required; use BufferedTokenStream", e);
}

Prevention

When it happens

Trigger: Calling seek() with an absolute index that is not represented by the current buffer window after sync; using an UnbufferedTokenStream subclass or stream manipulation that leaves p/currentTokenIndex inconsistent; or generic code assuming every TokenStream supports arbitrary seeks.

Common situations: Generic IntStream utilities, custom lookahead/backtracking engines, and code migrated from BufferedTokenStream. It commonly indicates the chosen stream implementation does not provide the required access model rather than a grammar bug.

Related errors


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