antlr/antlr4 · error · ArgumentException
cannot seek to negative index ${index}
Error message
cannot seek to negative index ${index} What it means
UnbufferedTokenStream.Seek(index) converts the absolute index to a buffer offset i = index - bufferStartIndex; when i < 0 it throws ArgumentException because the requested token lies before the first token currently retained in the rolling buffer. Unbuffered streams discard consumed tokens (the buffer is shifted when the last marker is released), so backward seeks are only possible within the retained window.
Source
Thrown at runtime/CSharp/src/UnbufferedTokenStream.cs:341
}
public virtual 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 ArgumentException("cannot seek to negative index " + index);
}
else
{
if (i >= n)
{
throw new NotSupportedException("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];
}View on GitHub (pinned to 7d5770395b)
Solutions
- Switch to CommonTokenStream (BufferedTokenStream) if you need random access to already-consumed tokens
- Before seeking, guard: if (index < stream.GetBufferStartIndex()) handle the missing-window case instead of seeking
- Keep an active Mark() for the whole period you may need to seek back — while numMarkers > 0 the buffer is not released
- For seeking to buffer start, use the current GetBufferStartIndex() rather than a stale absolute index
Example fix
// before
stream.Seek(oldTokenIndex); // may lie before buffer start
// after
int start = stream.GetBufferStartIndex();
if (oldTokenIndex >= start) {
stream.Seek(oldTokenIndex);
} else {
// token already discarded: re-lex from the source, or use BufferedTokenStream
} Defensive patterns
Strategy: validation
Validate before calling
// C#: only seek backward/forward within the retained window
int bufferStart = stream.GetBufferStartIndex();
if (index >= bufferStart) {
stream.Seek(index);
} else {
// tokens before bufferStart were discarded; re-lex or use CommonTokenStream
} Try / catch
try { stream.Seek(index); } catch (ArgumentException ex) when (ex.Message.Contains("negative index")) { // token already discarded: fall back to re-lexing input from the start, or switch the pipeline to CommonTokenStream } Prevention
- Prefer CommonTokenStream whenever any backward seek is possible in your control flow
- Hold a Mark() open across the whole window in which backward seeks may occur
- Never cache absolute token indices from an unbuffered stream across Release() calls
When it happens
Trigger: Seek() to any index < GetBufferStartIndex(): seeking backwards past tokens already dropped after the last Release() reduced numMarkers to 0 with p>0; seeking to a token index saved from an earlier token object; calling Seek(0) after the buffer has advanced.
Common situations: Adapting parser or error-recovery code written for BufferedTokenStream (which retains everything) to UnbufferedTokenStream; keeping token indices across long parses and jumping back to them; using tree/AST operations that call Seek on old positions.
Related errors
- seek to index outside buffer: ${index} not in ${bufferStartI
- interval ${interval} not in token buffer window: ${bufferSta
- cannot seek to negative index {index}
- seek to index outside buffer: {index} not in {bufferStartInd
- tokenSource cannot be null
AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14).
Data as JSON: /api/errors/e6488949d423698b.
Report an issue: GitHub.