{"record":{"id":"e6488949d423698b","repo":"antlr/antlr4","slug":"cannot-seek-to-negative-index-index-e64889","errorCode":null,"errorMessage":"cannot seek to negative index ${index}","messagePattern":"cannot seek to negative index (.+?)","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"runtime/CSharp/src/UnbufferedTokenStream.cs","lineNumber":341,"sourceCode":"        }\n\n        public virtual void Seek(int index)\n        {\n            // seek to absolute index\n            if (index == currentTokenIndex)\n            {\n                return;\n            }\n            if (index > currentTokenIndex)\n            {\n                Sync(index - currentTokenIndex);\n                index = Math.Min(index, GetBufferStartIndex() + n - 1);\n            }\n            int bufferStartIndex = GetBufferStartIndex();\n            int i = index - bufferStartIndex;\n            if (i < 0)\n            {\n                throw new ArgumentException(\"cannot seek to negative index \" + index);\n            }\n            else\n            {\n                if (i >= n)\n                {\n                    throw new NotSupportedException(\"seek to index outside buffer: \" + index + \" not in \" + bufferStartIndex + \"..\" + (bufferStartIndex + n));\n                }\n            }\n            p = i;\n            currentTokenIndex = index;\n            if (p == 0)\n            {\n                lastToken = lastTokenBufferStart;\n            }\n            else\n            {\n                lastToken = tokens[p - 1];\n            }","sourceCodeStart":323,"sourceCodeEnd":359,"githubUrl":"https://github.com/antlr/antlr4/blob/7d5770395bb7b02eb56e7c62662cb1d7c08f42a3/runtime/CSharp/src/UnbufferedTokenStream.cs#L323-L359","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nstream.Seek(oldTokenIndex); // may lie before buffer start\n\n// after\nint start = stream.GetBufferStartIndex();\nif (oldTokenIndex >= start) {\n    stream.Seek(oldTokenIndex);\n} else {\n    // token already discarded: re-lex from the source, or use BufferedTokenStream\n}","handlingStrategy":"validation","validationCode":"// C#: only seek backward/forward within the retained window\nint bufferStart = stream.GetBufferStartIndex();\nif (index >= bufferStart) {\n    stream.Seek(index);\n} else {\n    // tokens before bufferStart were discarded; re-lex or use CommonTokenStream\n}","typeGuard":null,"tryCatchPattern":"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 }","preventionTips":["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"],"tags":["antlr","csharp","token-stream","seek","rolling-buffer"],"backgroundTag":null,"analyzedSha":"7d5770395bb7b02eb56e7c62662cb1d7c08f42a3","analyzedAt":"2026-08-14T14:47:56.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}