antlr/antlr4 · error · IllegalArgumentException
cannot seek to negative index {index}
Error message
cannot seek to negative index {index} What it means
UnbufferedTokenStream.seek(index) accepts an absolute index only inside its retained moving window. It computes i = index - bufferStartIndex; i < 0 means the requested token was already flushed. Despite the wording, the printed index can be non-negative: seek(0) after the buffer start advanced also produces this exception.
Source
Thrown at runtime/Java/src/org/antlr/v4/runtime/UnbufferedTokenStream.java:256
public int index() {
return currentTokenIndex;
}
@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() {View on GitHub (pinned to 7d5770395b)
Solutions
- Call mark() before consuming tokens that you may need to seek back to; release it only after backtracking is complete.
- For random access or seek(0), use BufferedTokenStream/CommonTokenStream instead.
- Validate index >= 0 and never seek before the point where the stream was constructed/marked.
- Recreate the token source and stream when old tokens must be replayed and were not retained.
Example fix
// before
while (tokens.LA(1) != Token.EOF) tokens.consume();
tokens.seek(0); // old tokens were flushed
// after
int marker = tokens.mark(); // mark before consuming
try {
while (tokens.LA(1) != Token.EOF) tokens.consume();
tokens.seek(0);
} finally {
tokens.release(marker);
} Defensive patterns
Strategy: validation
Validate before calling
static boolean canSeek(UnbufferedTokenStream<Token> tokens, int index, boolean haveProtectingMark) {
if (index < 0) return false;
return index >= tokens.index() || haveProtectingMark;
} Type guard
static boolean supportsArbitraryBackwardSeek(TokenStream tokens) {
return tokens instanceof BufferedTokenStream;
} Try / catch
try {
tokens.seek(index);
} catch (IllegalArgumentException e) {
// token is outside the retained unbuffered window; replay from a new stream
} Prevention
- Mark before consuming any token you may revisit.
- Use BufferedTokenStream for seek(0) and arbitrary backtrack scenarios.
- Do not call stream reset routines after releasing the protecting mark.
When it happens
Trigger: Calling seek(0) after consuming tokens without an active marker; seeking backward to any index before getBufferStartIndex(); passing a negative index; or calling a reset/backtracking routine after the unbuffered stream has flushed old tokens.
Common situations: Porting random-access code from BufferedTokenStream to UnbufferedTokenStream, replaying a token range after parsing, and custom backtracking that forgot to mark the region before consuming.
Related errors
- release() called with an invalid marker.
- seek to index outside buffer: {index} not in {bufferStartInd
- cannot consume EOF
- Unbuffered stream cannot know its size
- interval {interval} not in token buffer window: {bufferStart
AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14).
Data as JSON: /api/errors/98ad624e4776b22e.
Report an issue: GitHub.