antlr/antlr4 · error · ArgumentException

cannot seek to negative index

Error message

cannot seek to negative index ${index}

What it means

UnbufferedCharStream keeps only a rolling window of already unread data. Seek() maps the requested absolute index to a buffer offset; a negative offset means the position was discarded and cannot be restored. Backward seeks are therefore illegal once the needed data has left the buffer.

Solutions

  1. Do not seek backward on UnbufferedCharStream; process input in one forward pass.
  2. Keep a Mark() active for any earlier position that must remain seekable and release it only after seeking is complete.
  3. Use AntlrInputStream (fully buffered characters) when backward seeks are required.

Example fix

// before
var chars = new UnbufferedCharStream(reader, 4096);
chars.Seek(0);

// after
var chars = new AntlrInputStream(reader); // supports random access
chars.Seek(0);
Defensive patterns

Strategy: validation

Validate before calling

if (targetIndex < chars.Index && randomAccessRequired)
    chars = new AntlrInputStream(reader); // or retain a Mark()

Type guard

static bool SupportsBackwardSeek(ICharStream stream) => stream is not UnbufferedCharStream;

Try / catch

try { chars.Seek(index); }
catch (ArgumentException ex) when (ex.Message.StartsWith("cannot seek to negative index")) { /* use buffered stream or keep markers */ }

Prevention

When it happens

Trigger: Calling Seek(oldIndex) after consuming beyond the retained window; releasing the marker that protected old data and then seeking back; or custom code assuming ICharStream supports arbitrary random access.

Common situations: Backtracking or reset logic is used with an unbuffered stream; a parser strategy requires rewinding; or migration from AntlrInputStream/CommonTokenStream to UnbufferedCharStream without preserving markers.

Related errors


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

Appendix: source

Thrown at runtime/CSharp/src/UnbufferedCharStream.cs:385

        /// <c>index-bufferStartIndex</c>
        /// .
        /// </remarks>
        public virtual void Seek(int index)
        {
            if (index == currentCharIndex)
            {
                return;
            }
            if (index > currentCharIndex)
            {
                Sync(index - currentCharIndex);
                index = Math.Min(index, BufferStartIndex + n - 1);
            }
            // index == to bufferStartIndex should set p to 0
            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;
            currentCharIndex = index;
            if (p == 0)
            {
                lastChar = lastCharBufferStart;
            }
            else
            {
                lastChar = data[p - 1];
            }

View on GitHub (pinned to 7d5770395b)