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

  1. Switch to CommonTokenStream (BufferedTokenStream) if you need random access to already-consumed tokens
  2. Before seeking, guard: if (index < stream.GetBufferStartIndex()) handle the missing-window case instead of seeking
  3. Keep an active Mark() for the whole period you may need to seek back — while numMarkers > 0 the buffer is not released
  4. 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

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


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